Obtenir le nom de la classe à partir d'une méthode statique en Java
Comment peut-on obtenir le nom de la classe à partir d'une méthode statique de la classe. Par exemple
public class MyClass {
public static String getClassName() {
String name = ????; // what goes here so the string "MyClass" is returned
return name;
}
}
pour le mettre en contexte, je veux en fait retourner le nom de la classe comme faisant partie d'un message dans une exception.
15 réponses
afin de soutenir le remaniement correctement (renommer la classe), alors vous devez utiliser l'un ou l'autre:
MyClass.class.getName(); // full name with package
ou (merci à @James Van Huis ):
MyClass.class.getSimpleName(); // class name and no more
Faire ce toolkit dit. Ne faites rien de ce genre:
return new Object() { }.getClass().getEnclosingClass();
en Java 7+ vous pouvez le faire dans la méthode statique / les champs:
MethodHandles.lookup().lookupClass()
Cette instruction fonctionne très bien:
Thread.currentThread().getStackTrace()[1].getClassName();
Vous pourriez faire quelque chose de vraiment doux en utilisant JNI comme ceci:
MyObject.java:
public class MyObject
{
static
{
System.loadLibrary( "classname" );
}
public static native String getClassName();
public static void main( String[] args )
{
System.out.println( getClassName() );
}
}
puis:
javac MyObject.java
javah -jni MyObject
puis:
MyObject.c:
#include "MyObject.h"
JNIEXPORT jstring JNICALL Java_MyObject_getClassName( JNIEnv *env, jclass cls )
{
jclass javaLangClass = (*env)->FindClass( env, "java/lang/Class" );
jmethodID getName = (*env)->GetMethodID( env, javaLangClass, "getName",
"()Ljava/lang/String;" );
return (*env)->CallObjectMethod( env, cls, getName );
}
compilez ensuite le code dans une bibliothèque partagée appelée libclassname.so
et lancez le java!
*rire
donc, nous avons une situation où nous avons besoin d'obtenir statiquement un objet de classe ou un nom complet/simple de classe sans un usage explicite de la syntaxe MyClass.class
.
il peut être très pratique dans certains cas, par exemple Logger instance pour les 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 pour obtenir cette information:
-
new Object(){}.getClass().getEnclosingClass();
noté par Tom Hawtin-tackline -
getClassContext()[0].getName();
de laSecurityManager
noté par Christoffer -
new Throwable().getStackTrace()[0].getClassName();
par comte ludwig -
Thread.currentThread().getStackTrace()[1].getClassName();
de Keksi -
et, enfin, génial
MethodHandles.lookup().lookupClass();
de Rein
J'ai préparé un jmh benchmark pour toutes les variantes et 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();
- dans le cas où vous avez besoin de cette fonctionnalité pour Android ou Java 6, Vous pouvez utiliser la seconde la 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 ne voulez pas que votre bytecode gonfle en raison de tonnes de classes anonymes –
SecurityManager
est votre ami (troisième meilleure option).mais vous ne pouvez pas simplement appeler
getClassContext()
– il est protégé dans la classeSecurityManager
. Vous aurez besoin d'un helper classe comme ceci:
// 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 le
getStackTrace()
d'exception ou leThread.currentThread()
. très inefficace et ne peut retourner que le nom de la classe comme unString
, pas leClass<*>
instance.
P. S.
si vous vous voulez créer une instance logger pour les utils Kotlin statiques (comme moi :), vous pouvez utiliser cet 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())
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
}
j'utilise ceci pour initialiser le Logger Log4j en haut de mes classes (ou annoter).
PRO: Throwable est déjà chargé et vous pourriez économiser des ressources en n'utilisant pas le Gestionnaire de sécurité "IO heavy".
CON: certains se demandent si cela fonctionnera pour toutes les JVM.
// Log4j . Logger --- Get class name in static context by creating an anonymous Throwable and
// getting the top of its stack-trace.
// NOTE you must use: getClassName() because getClass() just returns StackTraceElement.class
static final Logger logger = Logger.getLogger(new Throwable() .getStackTrace()[0].getClassName());
abuser le Gestionnaire de la sécurité
System.getSecurityManager().getClassContext()[0].getName();
ou, si elle n'est pas définie, utiliser une classe intérieure qui l'étend (exemple ci-dessous honteusement copié de Real's HowTo ):
public static class CurrentClassGetter extends SecurityManager {
public String getClassName() {
return getClassContext()[1].getName();
}
}
si vous voulez le nom complet du paquet avec, appelez:
String name = MyClass.class.getCanonicalName();
si vous ne voulez que le dernier élément, appelez:
String name = MyClass.class.getSimpleName();
Utilisation mot à mot de la classe de l'appelant comme MyClass.class.getName()
fait réellement le travail, mais est susceptible de copier/coller des erreurs si vous propagez ce code à de nombreuses classes/sous-classes où vous avez besoin de ce nom de classe.
Et Tom Hawtin la recette de n'est en fait pas mal, on a juste à faire cuire le droit chemin :)
dans le cas où vous avez une classe de base avec une méthode statique qui peut être appelé à partir de sous-classes, et cette méthode statique doit connaître la classe réelle de l'appelant, cela peut être atteint comme le suivant:
class BaseClass {
static sharedStaticMethod (String callerClassName, Object... otherArgs) {
useCallerClassNameAsYouWish (callerClassName);
// and direct use of 'new Object() { }.getClass().getEnclosingClass().getName()'
// instead of 'callerClassName' is not going to help here,
// as it returns "BaseClass"
}
}
class SubClass1 extends BaseClass {
static someSubclassStaticMethod () {
// this call of the shared method is prone to copy/paste errors
sharedStaticMethod (SubClass1.class.getName(),
other_arguments);
// and this call is safe to copy/paste
sharedStaticMethod (new Object() { }.getClass().getEnclosingClass().getName(),
other_arguments);
}
}
Un refactoring-sûr, couper et coller,-solution de sécurité qui évite la définition ad hoc de classes ci-dessous.
écrire une méthode statique qui récupère le nom de classe en prenant soin d'inclure le nom de classe dans le nom de méthode:
private static String getMyClassName(){
return MyClass.class.getName();
}
alors rappelez - le dans votre méthode statique:
public static void myMethod(){
Tracer.debug(getMyClassName(), "message");
}
Refactoring sécurité en évitant l'utilisation de chaînes, couper et coller, la sécurité est accordé parce que si vous coupez et collez l'appelant de la méthode vous ne trouverez pas le getMyClassName () dans la classe cible "MyClass2", donc vous serez forcé de le redéfinir et de le mettre à jour.
depuis la question quelque chose comme " ceci.classe "au lieu de" ClassName.de la classe? est marqué comme un double pour celui-ci (ce qui est discutable parce que cette question Est au sujet de la classe plutôt que le nom de classe), je poste la réponse ici:
class MyService {
private static Class thisClass = MyService.class;
// or:
//private static Class thisClass = new Object() { }.getClass().getEnclosingClass();
...
static void startService(Context context) {
Intent i = new Intent(context, thisClass);
context.startService(i);
}
}
il est important de définir thisClass
comme privé parce que:
1) Il ne doit pas être hérité: les classes dérivées doivent soit définir leur propre thisClass
ou de produire un message d'erreur
2) les références provenant d'autres classes devraient être faites comme ClassName.class
plutôt que ClassName.thisClass
.
avec thisClass
défini, l'accès au nom de classe devient:
thisClass.getName()
j'avais besoin du nom de classe dans les méthodes statiques de classes multiples donc j'ai implémenté une classe JavaUtil avec la méthode suivante:
public static String getClassName() {
String className = Thread.currentThread().getStackTrace()[2].getClassName();
int lastIndex = className.lastIndexOf('.');
return className.substring(lastIndex + 1);
}
j'Espère que ça aidera !
j'ai utilisé ces deux approches pour static
et non static
scénario:
classe principale:
//For non static approach
public AndroidLogger(Object classObject) {
mClassName = classObject.getClass().getSimpleName();
}
//For static approach
public AndroidLogger(String className) {
mClassName = className;
}
comment fournir le nom de classe:
non statique de la façon suivante:
private AndroidLogger mLogger = new AndroidLogger(this);
voie statique:
private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());
si vous utilisez la réflexion, vous pouvez obtenir l'objet de la méthode et ensuite:
method.getDeclaringClass().getName()
Pour obtenir la Méthode elle-même, vous pouvez probablement utiliser:
Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod ("method name", parameterTypes)