[英]Getting the class name from a static method in 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上层函数(在这种情况下,kotlin 创建了一个无法从 kotlin 代码访问的静态 Java 类)。
我们有几种不同的变体来获取此信息:
new Object(){}.getClass().getEnclosingClass();
汤姆·霍廷( Tom Hawtin)指出- tackline
getClassContext()[0].getName();
来自SecurityManager
克里斯托弗指出
new Throwable().getStackTrace()[0].getClassName();
路德维希伯爵
Thread.currentThread().getStackTrace()[1].getClassName();
来自凯克西
最后真棒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()
或Thread.currentThread()
的最后两个变体。 效率非常低,只能返回类名作为String
,而不是Class<*>
实例。如果你想为静态 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 来做一些非常棒的事情:
我的对象.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
然后:
我的对象.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 繁重”的 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()
这样逐字使用调用者的类实际上可以完成这项工作,但是如果将此代码传播到需要此类名称的众多类/子类,则很容易出现复制/粘贴错误。
而且汤姆·霍廷的食谱其实还不错,只需要用正确的方式烹饪即可:)
如果您有一个带有可以从子类调用的静态方法的基类,并且该静态方法需要知道实际调用者的类,则可以通过以下方式实现:
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(),因此您将被迫重新定义和更新它。
由于这个问题像 `this.class` 而不是 `ClassName.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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.