programing

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

randomtip 2022. 8. 30. 21:52
반응형

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

Java 7+ 에서는, 이것을 스태틱한 메서드/필드로 실행할 수 있습니다.

MethodHandles.lookup().lookupClass()

@toolkit의 지시에 따릅니다.다음과 같은 작업을 수행하지 마십시오.

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

(편집: 이 답변이 작성된 지 한참 후에 나온 Java 버전을 사용하는 경우 @Rein).

클래스 풀 이름을 해야 하는 상황이 .MyClass.class구문을 사용합니다.

를 들어 kotlin 상위 수준 함수의 logger 인스턴스(이 경우 kotlin 코드에서 액세스할 수 없는 정적 Java 클래스 생성)와 같이 경우에 따라서는 매우 편리할 수 있습니다.

이 정보를 입수하기 위한 몇 가지 다른 종류가 있습니다.

  1. new Object(){}.getClass().getEnclosingClass();
    Tom Hawtin에 의해 언급 - 태클라인

  2. getClassContext()[0].getName(); SecurityManager
    Christofer가 지적한

  3. new Throwable().getStackTrace()[0].getClassName();
    루드비히 백작에 의하면

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

  5. 멋지다
    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 후후 java java java java!
 MethodHandles.lookup().lookupClass();
  1. Android 또는 Java 6에서 이 기능이 필요한 경우 차선책을 사용할 수 있습니다.속도도 빠르지만 사용 장소마다 익명의 클래스를 만듭니다.
 new Object(){}.getClass().getEnclosingClass();
  1. 한 경우, 그리고 클래스로 인해 오르는 것을 않는 :SecurityManager친구(제3의 선택지)입니다.

    하지만 너 그렇다고전화만할 순 없어라고 부를 수 없습니다.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()예외 또는예외 또는 예외로부터에서Thread.currentThread()매우 비효율적이며 클래스 이름만 반환할 수 있습니다.String,,가지 않아니라Class<*>사례.사례.


추신.

static kotlin utils용으로 로거인스턴스를 작성하는 경우 다음 도우미를 사용할 수 있습니다.

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.so그리고 커피를 뛰어!

* 낄낄거리다

이를 사용하여 클래스 맨 위에 있는 Log4j Logger를 초기화하거나 주석을 달 수 있습니다.

PRO: 던질 수 있는 파일이 이미 로드되어 있으므로 "를 사용하지 않으면 리소스를 절약할 수 있습니다.I/O가 많은" Security Manager.

반대: 이것이 모든 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()); 

Security Manager 남용

System.getSecurityManager().getClassContext()[0].getName();

또는 설정되지 않은 경우 이를 확장하는 내부 클래스를 사용합니다(아래 예에서는 Real의 HowTo에서 수치스럽게 복사).

public static class CurrentClassGetter extends SecurityManager {
    public String getClassName() {
        return getClassContext()[1].getName(); 
    }
}

패키지 이름 전체를 포함하려면 다음 번호로 문의하십시오.

String name = MyClass.class.getCanonicalName();

마지막 요소만 필요한 경우 다음 연락처로 문의하십시오.

String name = MyClass.class.getSimpleName();

같은 호출자의 클래스의Verbatim 사용이다.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()

Method 자체를 취득하려면 , 다음의 방법을 사용할 수 있습니다.

Class<?> c = Class.forName("class name");
Method  method = c.getDeclaredMethod ("method name", parameterTypes)

언급URL : https://stackoverflow.com/questions/936684/getting-the-class-name-from-a-static-method-in-java

반응형