Wir haben also eine Situation, in der wir ein Klassenobjekt oder einen vollständigen / einfachen Klassennamen ohne explizite Verwendung von statisch abrufen müssen MyClass.class
Syntax abrufen müssen.
In einigen Fällen kann es sehr praktisch sein, z. B. eine Logger-Instanz für die Kotlin Funktionen der oberen Ebene (in diesem Fall erstellt kotlin eine statische Java-Klasse, auf die über den kotlin-Code nicht zugegriffen werden kann).
Wir haben verschiedene Varianten, um diese Informationen zu erhalten:
new Object(){}.getClass().getEnclosingClass();
notiert von Tom Hawtin - Tackline
getClassContext()[0].getName();
aus dem SecurityManager
von Christoffer notierten
new Throwable().getStackTrace()[0].getClassName();
von Graf Ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
von Keksi
und schließlich super
MethodHandles.lookup().lookupClass();
von Rein
Ich habe eine vorbereitet jmh Benchmark für alle Varianten und Ergebnisse sind:
# 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
Schlussfolgerungen
- Beste Variante , ziemlich sauber und ungeheuer schnell.
Verfügbar nur seit Java 7 und Android API 26!
MethodHandles.lookup().lookupClass();
- Wenn Sie diese Funktionalität für Android oder Java 6 benötigen, können Sie die zweitbeste Variante verwenden. Es ist auch ziemlich schnell, erstellt aber an jedem Verwendungsort eine anonyme Klasse :(
new Object(){}.getClass().getEnclosingClass();
Wenn Sie es an vielen Orten benötigen und nicht möchten, dass Ihr Bytecode aufgrund unzähliger anonymer Klassen aufgebläht wird, SecurityManager
ist dies Ihr Freund (drittbeste Option).
Aber Sie können nicht einfach anrufen getClassContext()
- es ist in der SecurityManager
Klasse geschützt . Sie benötigen eine Helferklasse wie diese:
// 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())
}
- Sie müssen wahrscheinlich nie die letzten beiden Varianten verwenden, die auf der
getStackTrace()
from-Ausnahme oder der from-Ausnahme basieren Thread.currentThread()
. Sehr ineffizient und kann nur den Klassennamen als String
, nicht die Class<*>
Instanz zurückgeben.
PS
Wenn Sie eine Logger-Instanz für statische Kotlin-Utils erstellen möchten (wie ich :), können Sie diesen Helfer verwenden:
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())
Anwendungsbeispiel:
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
}
try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();
}