Quindi, abbiamo una situazione in cui dobbiamo ottenere staticamente un oggetto classe o un nome completo / semplice di classe senza un uso esplicito della MyClass.class
sintassi.
Può essere davvero utile in alcuni casi, ad esempio l'istanza del logger per Kotlin funzioni di livello superiore (in questo caso kotlin crea una classe Java statica non accessibile dal codice kotlin).
Abbiamo alcune varianti per ottenere queste informazioni:
new Object(){}.getClass().getEnclosingClass();
notato da Tom Hawtin - tackline
getClassContext()[0].getName();
dal SecurityManager
noto di Christoffer
new Throwable().getStackTrace()[0].getClassName();
dal conte Ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
da Keksi
e finalmente fantastico
MethodHandles.lookup().lookupClass();
da Rein
Ho preparato un JMH benchmark per tutte le varianti e risultati sono:
# 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
conclusioni
- La migliore variante da usare , piuttosto pulita e mostruosamente veloce.
Disponibile solo da Java 7 e Android API 26!
MethodHandles.lookup().lookupClass();
- Nel caso in cui sia necessaria questa funzionalità per Android o Java 6, è possibile utilizzare la seconda variante migliore. È anche piuttosto veloce, ma crea una classe anonima in ogni luogo di utilizzo :(
new Object(){}.getClass().getEnclosingClass();
Se ne hai bisogno in molti luoghi e non vuoi che il tuo bytecode si gonfi a causa di tonnellate di classi anonime, SecurityManager
è il tuo amico (terza opzione migliore).
Ma non puoi semplicemente chiamare getClassContext()
: è protetto in SecurityManager
classe. Avrai bisogno di una classe di supporto come questa:
// 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())
}
- Probabilmente non avrai mai bisogno di usare le ultime due varianti in base
getStackTrace()
all'eccezione from o alla Thread.currentThread()
. Molto inefficiente e può restituire solo il nome della classe come a String
, non l' Class<*>
istanza.
PS
Se vuoi creare un'istanza del logger per utility kotlin statiche (come me :), puoi usare questo 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())
Esempio di utilizzo:
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();
}