如何从该类中的静态方法获取类的名称。例如
public class MyClass {
public static String getClassName() {
String name = ????; // what goes here so the string "MyClass" is returned
return name;
}
}
要把它放在上下文中,我实际上想要将类名作为异常中消息的一部分返回。
答案 0 :(得分:215)
为了正确支持重构(重命名类),您应该使用:
MyClass.class.getName(); // full name with package
或(感谢@James Van Huis):
MyClass.class.getSimpleName(); // class name and no more
答案 1 :(得分:117)
做什么工具包说。不要做这样的事情:
return new Object() { }.getClass().getEnclosingClass();
答案 2 :(得分:76)
在Java 7+中,您可以在静态方法/字段中执行此操作:
MethodHandles.lookup().lookupClass()
答案 3 :(得分:40)
因此,当我们需要静态获取类对象或类完整/简单名称而没有明确使用MyClass.class
语法时,我们就会出现这种情况。
在某些情况下,它可以非常方便,例如kotlin上层函数的logger实例(在这种情况下,kotlin创建了一个无法从kotlin代码访问的静态Java类)。
我们有一些不同的变体来获取此信息:
new Object(){}.getClass().getEnclosingClass();
由Tom Hawtin - tackline
getClassContext()[0].getName();
的 SecurityManager
由Christoffer
new Throwable().getStackTrace()[0].getClassName();
count ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
来自Keksi
最后很棒
MethodHandles.lookup().lookupClass();
来自Rein
我已为所有变体准备了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创建一个logger实例(比如我:),你可以使用这个帮助器:
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
}
答案 4 :(得分:37)
此说明正常:
Thread.currentThread().getStackTrace()[1].getClassName();
答案 5 :(得分: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.so
的共享库并运行java!
*笑声
答案 6 :(得分:21)
我使用它来启动我的类顶部的Log4j Logger(或注释)。
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());
答案 7 :(得分:14)
滥用SecurityManager
System.getSecurityManager().getClassContext()[0].getName();
或者,如果没有设置,请使用扩展它的内部类(下面的例子从Real's HowTo羞耻地复制):
public static class CurrentClassGetter extends SecurityManager {
public String getClassName() {
return getClassContext()[1].getName();
}
}
答案 8 :(得分:9)
如果您想要整个包名,请致电:
String name = MyClass.class.getCanonicalName();
如果您只想要最后一个元素,请致电:
String name = MyClass.class.getSimpleName();
答案 9 :(得分:5)
逐字使用调用者的类MyClass.class.getName()
实际上可以完成这项工作,但如果将此代码传播到需要此类名的许多类/子类,则很容易复制/粘贴错误。
Tom Hawtin's recipe实际上并不坏,只需要以正确的方式烹饪:)
如果你有一个带有可以从子类调用的静态方法的基类,并且这个静态方法需要知道实际调用者的类,可以像下面这样实现:
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);
}
}
答案 10 :(得分:4)
重构安全,剪切和粘贴安全的解决方案,可避免在下面定义ad-hoc类。
编写一个静态方法,用于恢复类名称,并注意在方法名称中包含类名:
private static String getMyClassName(){
return MyClass.class.getName();
}
然后在静态方法中调用它:
public static void myMethod(){
Tracer.debug(getMyClassName(), "message");
}
通过避免使用字符串来提供重构安全性,授予剪切和粘贴安全性,因为如果您剪切和粘贴调用方法,则在目标“MyClass2”类中找不到getMyClassName(),因此您将成为被迫重新定义和更新它。
答案 11 :(得分:3)
由于问题Something like `this.class` instead of `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)其他类的引用应该以{{1}}而不是ClassName.class
完成。
定义ClassName.thisClass
后,对类名的访问将变为:
thisClass
答案 12 :(得分:1)
我需要多个类的静态方法中的类名,所以我使用以下方法实现了一个JavaUtil类:
public static String getClassName() {
String className = Thread.currentThread().getStackTrace()[2].getClassName();
int lastIndex = className.lastIndexOf('.');
return className.substring(lastIndex + 1);
}
希望它会有所帮助!
答案 13 :(得分:0)
我已将这两种方法用于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());
答案 14 :(得分:-1)
如果您正在使用反射,则可以获取Method对象,然后:
method.getDeclaringClass().getName()
要获取Method本身,您可以使用:
Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod ("method name", parameterTypes)