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


244

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

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

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


try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();}
arminvanbuuren

답변:


231

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

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

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

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

136
MyClass에 대한 지식을 하드 코딩하려면 String name = "MyClass"; !
John Topley

111
그러나 IDE에서 클래스 이름을 리팩터링하면 제대로 작동하지 않습니다.
James Van Huis

9
진실. MyClass.class이 라인은 '변화 클래스 이름'리팩토링으로 잊어되지 않습니다 보장되지만
툴킷

19
나는 소원 "이"그 "class.xxx는"이 클래스를 의미하는 중 인스턴스 또는 정적 코드에서 허용 된 것을, 자바에서 현재 클래스를 의미하는 정적 컨텍스트에서 일! 이에 대한 문제는 컨텍스트에서 MyClass가 장황하고 중복된다는 것입니다. 그러나 Java를 좋아하는 한 자세한 정보에 의존하는 것 같습니다.
Lawrence Dol

51
서브 클래스에서 정적 메소드를 호출하고 서브 클래스 이름을 원하면 어떻게합니까?
Edward Falk

120

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

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

7
클래스가 다른 클래스를 확장하면 실제 클래스는 반환하지 않고 기본 클래스 만 반환합니다.
Luis Soeiro

1
@LuisSoeiro 메서드가 정의 된 클래스를 반환한다고 생각합니다. 기본 클래스가 정적 컨텍스트에 어떻게 영향을 미치는지 잘 모르겠습니다.
Tom Hawtin-tackline

8
getClass ()가 정적 일 수없는 이유를 이해하지 못합니다. 이 "이디엄"은 필요하지 않습니다.
mmirwaldt

1
@mmirwaldt getClass는 런타임 유형을 반환하므로 정적 일 수 없습니다.
Tom Hawtin-tackline

5
이것이 실제 답변입니다. 코드에 클래스 이름을 직접 작성할 필요가 없기 때문입니다.
Bobs

99

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

MethodHandles.lookup().lookupClass()

나는 말할거야 Reflection.getCallerClass(). 그러나 'sun'패키지에 있다는 경고를 표시합니다. 따라서 이것은 더 나은 해결책 일 수 있습니다.
Foumpie

1
@Foumpie : Java 9는이 비공식적 인 Reflection.getCallerClass()것을 대체 할 공식 API를 도입 할 예정 입니다. 그의 사소한 작업, 예를 들어 Optional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());, 그것은 조금 더 복잡 하지만, 그것은 훨씬 더 강력하다는 사실과 관련이 있습니다.
Holger

6
이것은 가장 좋은 해결책입니다. 실제 클래스 이름을 지정할 필요가 없으며 모호하지 않으며 해킹이 아니며 Artyom Krivolapov의 게시물에 따르면 아래에서 가장 빠른 접근 방법입니다.
skomisa

@Rein 런타임 클래스가 기본 클래스에서 호출 된 경우 런타임 클래스를 얻는 방법이 있습니까?
Glide

59

따라서 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
}

38

이 지침은 잘 작동합니다.

Thread.currentThread().getStackTrace()[1].getClassName();

5
정말 느려질 수 있으므로주의하십시오. 그러나 복사하여 붙여 넣을 수 있습니다.
Gábor Lipták

1
이것은 사용할 때마다 객체 또는 스레드를 만들 필요가 없다는 추가 이점이 있습니다.
Erel Segal-Halevi

5
@ErelSegalHalevi 그것은 비록 백그라운드에서 많은 StackTraceElement를 생성합니다 :(
Navin

4
소스 코드를 보면에서 호출되는 경우 Thread.getStackTrace()외에는 아무것도 수행하지 않음 return (new Exception()).getStackTrace();을 알 수 currentThread()있습니다. @count ludwig의 솔루션은 동일한 것을 달성하는 더 직접적인 방법입니다.
T-Bull

34

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

*킬킬 웃음


왜 이것이 내장되어 있지 않습니까?
ggb667

영리하고 유머 감사합니다. 연고는 C 함수의 기본 이름에 Java_MyObject_getClassName이름이 포함되어 있습니다. 그 방법은 JNI를 사용하는 것 RegisterNatives입니다. 물론 JNI로 피드를 제공해야 FindClass(env, 'com/example/MyObject')하므로 아무 것도 이길 수 없습니다.
Renate

2
@Renate, 그래서이 전체 답변은 실제로 농담입니까? 그렇다면 다른 사람들을 도와야하므로 무고한 사람들을 함정에 빠뜨리지 마십시오.
Stéphane Gourichon

글쎄, 그것은 내 농담이 아니었고 나는 그것이 농담임을 지적했다. 이 시나리오의 사용 사례는 일반적으로 로그에서 클래스를 식별하기위한 것입니다. 모든 복잡성을 제거하면 대개 다음 중 하나를 수행합니다. private static final String TAG = "MyClass"또는 private static final String TAG = MyClass.class.getSimpleName();두 번째는 IDE를 사용하여 전역 클래스 이름을 바꾸는 데 더 친숙합니다.
Renate

20

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

jvm이 귀찮게하지 않도록 자신 만의 예외 클래스를 만드십시오 : jhocr.googlecode.com/svn/trunk/src/main/java/com/googlecode/…
4F2E4A2E

1
이 솔루션만큼 끔찍한 것을 제안하려면 적어도 SecurityManager를 남용하는 것과 같은 끔찍한 솔루션 대신 MyClass.class.getName ()을 사용하는 자연스러운 솔루션과 전문가를 관련 시키십시오.
Søren Boisen 2016 년

더 많은 단점 : 너무 장황하다. slow (클래스가로드 될 때 한 번만 실행되기 때문에 실제로 작은 지점입니다).
toolforger

13

SecurityManager 남용

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

또는 설정되지 않은 경우 확장하는 내부 클래스를 사용하십시오 (아래 예제는 Real의 HowTo 에서 부끄럽게 복사되었습니다 ).

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

9

전체 패키지 이름을 사용하려면 다음을 호출하십시오.

String name = MyClass.class.getCanonicalName();

마지막 요소 만 원하면 다음을 호출하십시오.

String name = MyClass.class.getSimpleName();

5

호출자의 클래스를 그대로 사용하면 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);
  }
}

4

아래의 애드혹 클래스의 정의를 피하는 리팩토링, 안전 및 잘라 내기 방지 솔루션.

메소드 이름에 클래스 이름을 포함시켜야하는 클래스 이름을 복구하는 정적 메소드를 작성하십시오.

private static String getMyClassName(){
  return MyClass.class.getName();
}

그런 다음 정적 메소드에서 호출하십시오.

public static void myMethod(){
  Tracer.debug(getMyClassName(), "message");
}

문자열 사용을 피함으로써 리팩토링 안전이 제공됩니다. 잘라 내기 및 붙여 넣기 안전성이 부여됩니다. 호출자 메소드를 잘라내어 붙여 넣으면 대상 "MyClass2"클래스에서 getMyClassName ()을 찾을 수 없으므로 다시 정의하고 업데이트해야합니다.


3

`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()

1

여러 클래스의 정적 메소드에 클래스 이름이 필요하므로 다음 메소드로 JavaUtil 클래스를 구현했습니다.

public static String getClassName() {
    String className = Thread.currentThread().getStackTrace()[2].getClassName();
    int lastIndex = className.lastIndexOf('.');
    return className.substring(lastIndex + 1);
}

그것이 도움이되기를 바랍니다!


1
매직 넘버 2 (NullPointerException을 쉽게 초래할 수 있음) 때문에 이것을 사용하는 것은 좋지 않을뿐만 아니라 가상 머신의 정확성에 크게 의존하고 있습니다. 메소드의 javadoc에서 : * 일부 가상 머신은 경우에 따라 스택 추적에서 하나 이상의 스택 프레임을 생략 할 수 있습니다. 극단적 인 경우,이 스레드에 관한 스택 추적 정보가없는 가상 머신은이 메소드에서 길이가 0 인 배열을 리턴 할 수 있습니다. *
Shotgun

0

나는이 두 가지 접근법을 시나리오 staticnon 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());

-1

리플렉션을 사용하는 경우 Method 객체를 얻을 수 있습니다.

method.getDeclaringClass().getName()

메소드 자체를 얻으려면 다음을 사용할 수 있습니다.

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

3
"클래스 이름"이 무엇인지 어떻게 알 수 있습니까? :)
alfasin

1
Class.forName("class name")이미 당신에게 클래스를 제공합니다. 왜 메소드를 통해 검색 하시겠습니까?
Sergey Irisov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.