답변:
리팩토링을 올바르게 지원하려면 (클래스 이름 바꾸기) 다음 중 하나를 사용해야합니다.
MyClass.class.getName(); // full name with package
또는 ( @James Van Huis 덕분에 ) :
MyClass.class.getSimpleName(); // class name and no more
툴킷의 기능을 수행하십시오. 다음과 같이하지 마십시오 :
return new Object() { }.getClass().getEnclosingClass();
getClass
는 런타임 유형을 반환하므로 정적 일 수 없습니다.
Java 7 이상에서는 정적 메소드 / 필드에서이를 수행 할 수 있습니다.
MethodHandles.lookup().lookupClass()
Reflection.getCallerClass()
. 그러나 'sun'패키지에 있다는 경고를 표시합니다. 따라서 이것은 더 나은 해결책 일 수 있습니다.
Reflection.getCallerClass()
것을 대체 할 공식 API를 도입 할 예정 입니다. 그의 사소한 작업, 예를 들어 Optional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());
, 그것은 조금 더 복잡 하지만, 그것은 훨씬 더 강력하다는 사실과 관련이 있습니다.
따라서 MyClass.class
구문을 명시 적으로 사용하지 않고 클래스 객체 또는 클래스 전체 / 간단한 이름을 정적으로 가져와야하는 상황이 있습니다.
경우에 따라 로거 인스턴스와 같은 경우에 정말 유용 할 수 있습니다. 코 틀린 상위 레벨 함수 (이 경우 kotlin은 kotlin 코드에서 액세스 할 수없는 정적 Java 클래스를 작성 함)
이 정보를 얻기위한 몇 가지 변형이 있습니다.
new Object(){}.getClass().getEnclosingClass();
에 의해 지적 톰 Hawtin의 - tackline
getClassContext()[0].getName();
Christoffer 의 SecurityManager
언급 에서
new Throwable().getStackTrace()[0].getClassName();
에 의해 계산 루드비히
Thread.currentThread().getStackTrace()[1].getClassName();
에서 Keksi
그리고 마침내 Rein
MethodHandles.lookup().lookupClass();
에서 최고
나는 준비했다 jmh 모든 변형 및 결과에 대한 벤치 마크는 다음과 같습니다.
# Run complete. Total time: 00:04:18
Benchmark Mode Cnt Score Error Units
StaticClassLookup.MethodHandles_lookup_lookupClass avgt 30 3.630 ± 0.024 ns/op
StaticClassLookup.AnonymousObject_getClass_enclosingClass avgt 30 282.486 ± 1.980 ns/op
StaticClassLookup.SecurityManager_classContext_1 avgt 30 680.385 ± 21.665 ns/op
StaticClassLookup.Thread_currentThread_stackTrace_1_className avgt 30 11179.460 ± 286.293 ns/op
StaticClassLookup.Throwable_stackTrace_0_className avgt 30 10221.209 ± 176.847 ns/op
MethodHandles.lookup().lookupClass();
new Object(){}.getClass().getEnclosingClass();
많은 곳에서 필요하고 수많은 익명 클래스로 인해 바이트 코드가 부풀어 오르기를 원하지 않는다면 SecurityManager
친구입니다 (세 번째 최선의 선택).
그러나 당신은 단지 전화 할 수 없습니다 getClassContext()
– 그것은 SecurityManager
수업 에서 보호됩니다 . 다음과 같은 도우미 클래스가 필요합니다.
// Helper class
public final class CallerClassGetter extends SecurityManager
{
private static final CallerClassGetter INSTANCE = new CallerClassGetter();
private CallerClassGetter() {}
public static Class<?> getCallerClass() {
return INSTANCE.getClassContext()[1];
}
}
// Usage example:
class FooBar
{
static final Logger LOGGER = LoggerFactory.getLogger(CallerClassGetter.getCallerClass())
}
getStackTrace()
from 예외 또는 에 기반하여 마지막 두 변형을 사용할 필요는 없습니다 Thread.currentThread()
. 매우 비효율적이며 인스턴스 이름이 String
아닌 클래스 이름 만 반환 할 수 있습니다 Class<*>
.정적 kotlin 유틸리티 (예 : 나와 같은)에 대한 로거 인스턴스를 작성하려는 경우이 헬퍼를 사용할 수 있습니다.
import org.slf4j.Logger
import org.slf4j.LoggerFactory
// Should be inlined to get an actual class instead of the one where this helper declared
// Will work only since Java 7 and Android API 26!
@Suppress("NOTHING_TO_INLINE")
inline fun loggerFactoryStatic(): Logger
= LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())
사용 예 :
private val LOGGER = loggerFactoryStatic()
/**
* Returns a pseudo-random, uniformly distributed value between the
* given least value (inclusive) and bound (exclusive).
*
* @param min the least value returned
* @param max the upper bound (exclusive)
*
* @return the next value
* @throws IllegalArgumentException if least greater than or equal to bound
* @see java.util.concurrent.ThreadLocalRandom.nextDouble(double, double)
*/
fun Random.nextDouble(min: Double = .0, max: Double = 1.0): Double {
if (min >= max) {
if (min == max) return max
LOGGER.warn("nextDouble: min $min > max $max")
return min
}
return nextDouble() * (max - min) + min
}
이 지침은 잘 작동합니다.
Thread.currentThread().getStackTrace()[1].getClassName();
Thread.getStackTrace()
외에는 아무것도 수행하지 않음 return (new Exception()).getStackTrace();
을 알 수 currentThread()
있습니다. @count ludwig의 솔루션은 동일한 것을 달성하는 더 직접적인 방법입니다.
다음과 같이 JNI를 사용하여 정말 달콤한 것을 할 수 있습니다.
MyObject.java :
public class MyObject
{
static
{
System.loadLibrary( "classname" );
}
public static native String getClassName();
public static void main( String[] args )
{
System.out.println( getClassName() );
}
}
그때:
javac MyObject.java
javah -jni MyObject
그때:
MyObject.c :
#include "MyObject.h"
JNIEXPORT jstring JNICALL Java_MyObject_getClassName( JNIEnv *env, jclass cls )
{
jclass javaLangClass = (*env)->FindClass( env, "java/lang/Class" );
jmethodID getName = (*env)->GetMethodID( env, javaLangClass, "getName",
"()Ljava/lang/String;" );
return (*env)->CallObjectMethod( env, cls, getName );
}
그런 다음 C를 공유 라이브러리로 컴파일하고 libclassname.so
java!
*킬킬 웃음
Java_MyObject_getClassName
이름이 포함되어 있습니다. 그 방법은 JNI를 사용하는 것 RegisterNatives
입니다. 물론 JNI로 피드를 제공해야 FindClass(env, 'com/example/MyObject')
하므로 아무 것도 이길 수 없습니다.
private static final String TAG = "MyClass"
또는 private static final String TAG = MyClass.class.getSimpleName();
두 번째는 IDE를 사용하여 전역 클래스 이름을 바꾸는 데 더 친숙합니다.
이 클래스를 사용하여 클래스 상단에 Log4j 로거를 초기화하거나 주석을 달 수 있습니다.
PRO : Throwable이 이미로드되어 있으며 "IO heavy"SecurityManager를 사용하지 않으면 자원을 절약 할 수 있습니다.
CON : 이것이 모든 JVM에서 작동하는지에 대한 질문입니다.
// Log4j . Logger --- Get class name in static context by creating an anonymous Throwable and
// getting the top of its stack-trace.
// NOTE you must use: getClassName() because getClass() just returns StackTraceElement.class
static final Logger logger = Logger.getLogger(new Throwable() .getStackTrace()[0].getClassName());
SecurityManager 남용
System.getSecurityManager().getClassContext()[0].getName();
또는 설정되지 않은 경우 확장하는 내부 클래스를 사용하십시오 (아래 예제는 Real의 HowTo 에서 부끄럽게 복사되었습니다 ).
public static class CurrentClassGetter extends SecurityManager {
public String getClassName() {
return getClassContext()[1].getName();
}
}
호출자의 클래스를 그대로 사용하면 MyClass.class.getName()
실제로 작업을 수행하지만이 코드를이 클래스 이름이 필요한 수많은 클래스 / 서브 클래스에 전파하면 오류를 복사 / 붙여 넣기 쉽습니다.
그리고 Tom Hawtin의 요리법 은 실제로 나쁘지 않습니다. 단지 올바른 방법으로 요리해야합니다. :)
서브 클래스에서 호출 될 수있는 정적 메소드가있는 기본 클래스가 있고이 정적 메소드가 실제 호출자의 클래스를 알아야하는 경우 다음과 같이 수행 될 수 있습니다.
class BaseClass {
static sharedStaticMethod (String callerClassName, Object... otherArgs) {
useCallerClassNameAsYouWish (callerClassName);
// and direct use of 'new Object() { }.getClass().getEnclosingClass().getName()'
// instead of 'callerClassName' is not going to help here,
// as it returns "BaseClass"
}
}
class SubClass1 extends BaseClass {
static someSubclassStaticMethod () {
// this call of the shared method is prone to copy/paste errors
sharedStaticMethod (SubClass1.class.getName(),
other_arguments);
// and this call is safe to copy/paste
sharedStaticMethod (new Object() { }.getClass().getEnclosingClass().getName(),
other_arguments);
}
}
아래의 애드혹 클래스의 정의를 피하는 리팩토링, 안전 및 잘라 내기 방지 솔루션.
메소드 이름에 클래스 이름을 포함시켜야하는 클래스 이름을 복구하는 정적 메소드를 작성하십시오.
private static String getMyClassName(){
return MyClass.class.getName();
}
그런 다음 정적 메소드에서 호출하십시오.
public static void myMethod(){
Tracer.debug(getMyClassName(), "message");
}
문자열 사용을 피함으로써 리팩토링 안전이 제공됩니다. 잘라 내기 및 붙여 넣기 안전성이 부여됩니다. 호출자 메소드를 잘라내어 붙여 넣으면 대상 "MyClass2"클래스에서 getMyClassName ()을 찾을 수 없으므로 다시 정의하고 업데이트해야합니다.
`ClassName.class` 대신`this.class`와 같은 질문이 있습니까? 이 질문에 대한 중복으로 표시됩니다 (질문은 클래스 이름이 아닌 클래스에 관한 것이므로 논쟁의 여지가 있습니다).
class MyService {
private static Class thisClass = MyService.class;
// or:
//private static Class thisClass = new Object() { }.getClass().getEnclosingClass();
...
static void startService(Context context) {
Intent i = new Intent(context, thisClass);
context.startService(i);
}
}
정의하는 것이 중요하다 thisClass
으로 개인 : 때문에
파생 클래스 중 하나를 자신을 정의해야합니다 : 1)이 상속되지 않아야 thisClass
또는 오류 메시지가 생성
으로 수행해야합니다 2) 다른 클래스에서 참조를 ClassName.class
하기보다는 ClassName.thisClass
.
로 thisClass
정의 된 클래스 이름에 대한 액세스가된다 :
thisClass.getName()
여러 클래스의 정적 메소드에 클래스 이름이 필요하므로 다음 메소드로 JavaUtil 클래스를 구현했습니다.
public static String getClassName() {
String className = Thread.currentThread().getStackTrace()[2].getClassName();
int lastIndex = className.lastIndexOf('.');
return className.substring(lastIndex + 1);
}
그것이 도움이되기를 바랍니다!
나는이 두 가지 접근법을 시나리오 static
와 non static
시나리오 모두에 사용했습니다 .
메인 클래스 :
//For non static approach
public AndroidLogger(Object classObject) {
mClassName = classObject.getClass().getSimpleName();
}
//For static approach
public AndroidLogger(String className) {
mClassName = className;
}
수업 명 제공 방법 :
비 정적 방법 :
private AndroidLogger mLogger = new AndroidLogger(this);
정적 방법 :
private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());
리플렉션을 사용하는 경우 Method 객체를 얻을 수 있습니다.
method.getDeclaringClass().getName()
메소드 자체를 얻으려면 다음을 사용할 수 있습니다.
Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod ("method name", parameterTypes)
Class.forName("class name")
이미 당신에게 클래스를 제공합니다. 왜 메소드를 통해 검색 하시겠습니까?
try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();
}