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은 kotlin 코드에서 액세스 할 수없는 정적 Java 클래스를 작성 함) 와 같은 일부 경우에 매우 유용 할 수 있습니다 .
이 정보를 얻기위한 몇 가지 변형이 있습니다.
new Object(){}.getClass().getEnclosingClass();
에 의해 지적 톰 Hawtin의 - tacklinegetClassContext()[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
결론
- 사용하기에 가장 좋은 변형 , 오히려 깨끗하고 엄청나게 빠릅니다.
Java 7 및 Android API 26 이후에만 사용 가능합니다!
MethodHandles.lookup().lookupClass();
- Android 또는 Java 6에이 기능이 필요한 경우 두 번째 최상의 변형을 사용할 수 있습니다. 그것은 빠르고 너무 오히려입니다, 하지만 각각의 사용 대신에 익명의 클래스를 만듭니다 :(
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();
다음과 같이 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!
*킬킬 웃음
나는 이것을 사용하여 내 클래스 상단 (또는 주석)에서 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();
}
}
전체 패키지 이름을 사용하려면 다음을 호출하십시오.
String name = MyClass.class.getCanonicalName();
마지막 요소 만 원하면 다음을 호출하십시오.
String name = MyClass.class.getSimpleName();
호출자의 클래스를 그대로 사용하면 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)
참고 URL : https://stackoverflow.com/questions/936684/getting-the-class-name-from-a-static-method-in-java
'IT story' 카테고리의 다른 글
WCF 서비스에서 깨끗한 JSON을 어떻게 반환합니까? (0) | 2020.04.16 |
---|---|
선택한 텍스트 블록을 대문자로 만듭니다. (0) | 2020.04.15 |
Angularjs-ng-cloak / ng-show 요소가 깜박임 (0) | 2020.04.15 |
신속한 사전 키 배열 (0) | 2020.04.15 |
인 텐트의 모든 엑스트라 나열 (0) | 2020.04.15 |