如何从该类的静态方法中获取该类的名称。例如
public class MyClass {
public static String getClassName() {
String name = ????; // what goes here so the string "MyClass" is returned
return name;
}
}
为了把它放在上下文中,我实际上想在异常中返回类名作为消息的一部分。
如何从该类的静态方法中获取该类的名称。例如
public class MyClass {
public static String getClassName() {
String name = ????; // what goes here so the string "MyClass" is returned
return name;
}
}
为了把它放在上下文中,我实际上想在异常中返回类名作为消息的一部分。
当前回答
因此,当我们需要静态地获取类对象或类的完整/简单名称而不显式地使用MyClass.class语法时,我们会遇到这种情况。
在某些情况下,它真的很方便,例如kotlin高级函数的记录器实例(在这种情况下,kotlin创建了一个静态Java类,不能从kotlin代码中访问)。
我们有几个不同的变量来获得这个信息:
新对象(){}.getClass () .getEnclosingClass (); 汤姆·霍廷·塔克兰指出 getClassContext () [0] . getname ();来自SecurityManager 克里斯托弗注意到 ()新Throwable .getStackTrace () [0] .getClassName (); 路德维希伯爵 .getStackTrace Thread.currentThread () () [1] .getClassName (); 从Keksi 最后非常棒 .lookupClass MethodHandles.lookup () (); 从控制
jmh
benchmark for all variants and results are:# 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 <*>实例。
P.S.
如果你想为静态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
* @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
}
其他回答
一种重构安全、剪切和粘贴安全的解决方案,避免了下面定义的临时类。
写一个恢复类名的静态方法,注意在方法名中包含类名:
private static String getMyClassName(){
return MyClass.class.getName();
}
然后在你的静态方法中召回它:
public static void myMethod(){
Tracer.debug(getMyClassName(), "message");
}
重构安全性是通过避免使用字符串来实现的,剪切和粘贴安全性是被授予的,因为如果你剪切和粘贴调用者方法,你将在目标“MyClass2”类中找不到getMyClassName(),所以你将被迫重新定义和更新它。
像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);
}
}
为了正确地支持重构(重命名类),那么你应该使用:
MyClass.class.getName(); // full name with package
或者(感谢@James Van Huis):
MyClass.class.getSimpleName(); // class name and no more
因为问题是' 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定义为private很重要,因为: 1)它不能被继承:派生类必须要么定义自己的thisClass,要么产生错误消息 2)其他类的引用应该使用ClassName.class而不是ClassName.thisClass。
定义了thisClass后,对类名的访问变为:
thisClass.getName()
如果你想要整个包的名称,调用:
String name = MyClass.class.getCanonicalName();
如果你只想要最后一个元素,调用:
String name = MyClass.class.getSimpleName();