通常在打印日志的时候需要输出类名,普通类可以用this.getClass(),但是静态类没有this,直接写类名耦合度高。
参考了:
https://stackoverflow.com/questions/936684/getting-the-class-name-from-a-static-method-in-java
最有价值的回答:
So, we have a situation when we need to statically get class object or a class full/simple name without an explicit usage of
MyClass.class
syntax.It can be really handy in some cases, e.g. logger instance for the kotlin upper-level functions (in this case kotlin creates a static Java class not accessible from the kotlin code).
We have a few different variants for getting this info:
new Object(){}.getClass().getEnclosingClass();
noted by Tom Hawtin - tackline
getClassContext()[0].getName();
from theSecurityManager
noted by Christoffer
new Throwable().getStackTrace()[0].getClassName();
by count ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
from Keksiand finally awesome
MethodHandles.lookup().lookupClass();
from Rein
I've prepared a 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
Conclusions
- Best variant to use, rather clean and monstrously fast.
Available only since Java 7 and Android API 26!MethodHandles.lookup().lookupClass();
- In case you need this functionality for Android or Java 6, you can use the second best variant. It's rather fast too, but creates an anonymous class in each place of usage :(
new Object(){}.getClass().getEnclosingClass();
If you need it in many places and don't want your bytecode to bloat due to tons of anonymous classes –
SecurityManager
is your friend (third best option).But you can't just call
getClassContext()
– it's protected in theSecurityManager
class. You will need some helper class like this:// 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()) }
- You probably don't ever need to use last two variants based on the
getStackTrace()
from exception or theThread.currentThread()
. Very inefficient and can return only the class name as aString
, not theClass<*>
instance.P.S.
If you want to create a logger instance for static kotlin utils (like me :), you can use this helper:
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())
Usage example:
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 }
总结
Java7中的MethodHandles.lookup().lookupClass(),又快又简洁。
如果条件不允许可以退而求其次用new Object(){}.getClass().getEnclosingClass()
如果用Maven项目,默认是Source和Target都是1.5,需要加上,指定为1.7即可。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>