Donc, nous avons une situation où nous devons obtenir statiquement un objet de classe ou un nom complet / simple de classe sans utilisation explicite de la MyClass.class
syntaxe.
Il peut être très pratique dans certains cas, par exemple l'instance de l'enregistreur pour le kotlin fonctions de niveau supérieur (dans ce cas, kotlin crée une classe Java statique non accessible à partir du code kotlin).
Nous avons quelques variantes différentes pour obtenir ces informations:
new Object(){}.getClass().getEnclosingClass();
noté par Tom Hawtin - tackline
getClassContext()[0].getName();
de la SecurityManager
note de Christoffer
new Throwable().getStackTrace()[0].getClassName();
par le comte ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
de Keksi
et enfin génial
MethodHandles.lookup().lookupClass();
de Rein
J'ai préparé un jmh les références pour toutes les variantes et tous les résultats sont:
# 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
- Meilleure variante à utiliser , plutôt propre et monstrueusement rapide.
Disponible uniquement depuis Java 7 et Android API 26!
MethodHandles.lookup().lookupClass();
- Si vous avez besoin de cette fonctionnalité pour Android ou Java 6, vous pouvez utiliser la deuxième meilleure variante. C'est assez rapide aussi, mais crée une classe anonyme dans chaque lieu d'utilisation :(
new Object(){}.getClass().getEnclosingClass();
Si vous en avez besoin dans de nombreux endroits et que vous ne voulez pas que votre bytecode gonfle en raison de tonnes de classes anonymes - SecurityManager
c'est votre ami (troisième meilleure option).
Mais vous ne pouvez pas simplement appeler getClassContext()
- c'est protégé dans la SecurityManager
classe. Vous aurez besoin d'une classe d'aide comme celle-ci:
// 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())
}
- Vous n'avez probablement jamais besoin d'utiliser les deux dernières variantes basées sur l'
getStackTrace()
exception from ou la Thread.currentThread()
. Très inefficace et ne peut renvoyer que le nom de classe en tant que a String
, pas l' Class<*>
instance.
PS
Si vous souhaitez créer une instance d'enregistreur pour les utilitaires de kotlin statiques (comme moi :), vous pouvez utiliser cet assistant:
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())
Exemple d'utilisation:
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();
}