Entonces, tenemos una situación en la que necesitamos obtener estáticamente un objeto de clase o un nombre completo / simple de clase sin un uso explícito de MyClass.class
sintaxis.
Puede ser realmente útil en algunos casos, por ejemplo, la instancia del registrador para Kotlin funciones de nivel superior (en este caso, kotlin crea una clase Java estática no accesible desde el código de kotlin).
Tenemos algunas variantes diferentes para obtener esta información:
new Object(){}.getClass().getEnclosingClass();
señalado por Tom Hawtin - tackline
getClassContext()[0].getName();
de lo SecurityManager
señalado por Christoffer
new Throwable().getStackTrace()[0].getClassName();
por el conde ludwig
Thread.currentThread().getStackTrace()[1].getClassName();
de Keksi
y finalmente impresionante
MethodHandles.lookup().lookupClass();
de Rein
He preparado un jmh Los puntos de referencia para todas las variantes y resultados son:
# 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
Conclusiones
- La mejor variante para usar , bastante limpia y monstruosamente rápida.
¡Disponible solo desde Java 7 y Android API 26!
MethodHandles.lookup().lookupClass();
- En caso de que necesite esta funcionalidad para Android o Java 6, puede usar la segunda mejor variante. También es bastante rápido, pero crea una clase anónima en cada lugar de uso :(
new Object(){}.getClass().getEnclosingClass();
Si lo necesita en muchos lugares y no quiere que su código de bytes se hinche debido a toneladas de clases anónimas, SecurityManager
es su amigo (la tercera mejor opción).
Pero no puedes simplemente llamar getClassContext()
, está protegido en la SecurityManager
clase. Necesitarás una clase auxiliar como esta:
// 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())
}
- Probablemente no necesite usar las dos últimas variantes en función de la
getStackTrace()
excepción from o de Thread.currentThread()
. Muy ineficiente y puede devolver solo el nombre de la clase como a String
, no la Class<*>
instancia.
PD
Si desea crear una instancia de registrador para utilidades estáticas de kotlin (como yo :), puede usar este ayudante:
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())
Ejemplo de uso:
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();
}