[java] Java의 정적 메소드에서 클래스 이름 가져 오기

해당 클래스의 정적 메소드에서 클래스 이름을 얻는 방법은 무엇입니까? 예를 들어

public class MyClass {
    public static String getClassName() {
        String name = ????; // what goes here so the string "MyClass" is returned
        return name;
    }
}

문맥에 맞추기 위해 실제로 예외의 메시지의 일부로 클래스 이름을 반환하려고합니다.



답변

리팩토링을 올바르게 지원하려면 (클래스 이름 바꾸기) 다음 중 하나를 사용해야합니다.

 MyClass.class.getName(); // full name with package

또는 ( @James Van Huis 덕분에 ) :

 MyClass.class.getSimpleName(); // class name and no more


답변

툴킷의 기능을 수행하십시오. 다음과 같이하지 마십시오 :

return new Object() { }.getClass().getEnclosingClass();


답변

Java 7 이상에서는 정적 메소드 / 필드에서이를 수행 할 수 있습니다.

MethodHandles.lookup().lookupClass()


답변

따라서 MyClass.class구문을 명시 적으로 사용하지 않고 클래스 객체 또는 클래스 전체 / 간단한 이름을 정적으로 가져와야하는 상황이 있습니다.

경우에 따라 로거 인스턴스와 같은 경우에 정말 유용 할 수 있습니다. 상위 레벨 함수 (이 경우 kotlin은 kotlin 코드에서 액세스 할 수없는 정적 Java 클래스를 작성 함)

이 정보를 얻기위한 몇 가지 변형이 있습니다.

  1. new Object(){}.getClass().getEnclosingClass();
    에 의해 지적 톰 Hawtin의 – tackline

  2. getClassContext()[0].getName();ChristofferSecurityManager
    언급 에서

  3. new Throwable().getStackTrace()[0].getClassName();
    에 의해 계산 루드비히

  4. Thread.currentThread().getStackTrace()[1].getClassName();
    에서 Keksi

  5. 그리고 마침내 Rein
    MethodHandles.lookup().lookupClass();
    에서 최고

나는 준비했다 모든 변형 및 결과에 대한 벤치 마크는 다음과 같습니다.

# 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

결론

  1. 사용하기 가장 좋은 변형 , 오히려 깨끗하고 엄청나게 빠릅니다.
    Java 7 및 Android API 26 이후에만 사용 가능합니다!
 MethodHandles.lookup().lookupClass();
  1. Android 또는 Java 6에이 기능이 필요한 경우 두 번째 최상의 변형을 사용할 수 있습니다. 그것은 빠르고 너무 오히려입니다, 하지만 각각의 사용 대신에 익명의 클래스를 만듭니다 🙁
 new Object(){}.getClass().getEnclosingClass();
  1. 많은 곳에서 필요하고 수많은 익명 클래스로 인해 바이트 코드가 부풀어 오르기를 원하지 않는다면 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())
 }
  1. 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();


답변

다음과 같이 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.sojava!

*킬킬 웃음


답변

이 클래스를 사용하여 클래스 상단에 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());