Comment peut-on obtenir le nom de la classe à partir d’une méthode statique dans cette classe. Par exemple
public class MyClass { public static Ssortingng getClassName() { Ssortingng name = ????; // what goes here so the ssortingng "MyClass" is returned return name; } }
Pour le mettre en contexte, je souhaite en fait renvoyer le nom de la classe dans le cadre d’un message dans une exception.
Pour prendre en charge correctement le refactoring (renommer la classe), vous devez utiliser soit:
MyClass.class.getName(); // full name with package
ou (grâce à @James Van Huis ):
MyClass.class.getSimpleName(); // class name and no more
Faites ce que dit la boîte à outils. Ne faites rien comme ceci:
return new Object() { }.getClass().getEnclosingClass();
Dans Java 7+, vous pouvez le faire en méthodes / champs statiques:
MethodHandles.lookup().lookupClass()
Cette instruction fonctionne bien:
Thread.currentThread().getStackTrace()[1].getClassName();
Vous pouvez faire quelque chose de vraiment gentil en utilisant JNI comme ceci:
MyObject.java:
public class MyObject { static { System.loadLibrary( "classname" ); } public static native Ssortingng getClassName(); public static void main( Ssortingng[] args ) { System.out.println( getClassName() ); } }
puis:
javac MyObject.java javah -jni MyObject
puis:
MyObject.c:
#include "MyObject.h" JNIEXPORT jssortingng 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/Ssortingng;" ); return (*env)->CallObjectMethod( env, cls, getName ); }
Ensuite, comstackz le C dans une bibliothèque partagée appelée libclassname.so
et lancez le java!
*glousser
Nous avons donc une situation où nous devons obtenir statiquement un object de classe ou un nom de classe full / simple sans utiliser explicitement la syntaxe MyClass.class
.
Dans certains cas, cela peut être très pratique, par exemple une 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 cette information:
new Object(){}.getClass().getEnclosingClass();
noté par Tom Hawtin – ligne de touche
getClassContext()[0].getName();
à partir de SecurityManager
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 benchmark jmh 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
MethodHandles.lookup().lookupClass();
new Object(){}.getClass().getEnclosingClass();
Si vous en avez besoin dans de nombreux endroits et que vous ne voulez pas que votre bytecode se gonfle en raison des 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 classe SecurityManager
. 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()) }
getStackTrace()
de l’exception ou Thread.currentThread()
. Très inefficace et ne peut renvoyer que le nom de la classe en tant que Ssortingng
, pas l’instance de la Class<*>
. Si vous voulez créer une instance de journalisation pour les utilitaires statiques de kotlin (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 dissortingbuted 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 }
Je l’utilise pour lancer le Log4j Logger en haut de mes classes (ou annoter).
PRO: Throwable est déjà chargé et vous pouvez économiser des ressources en n’utilisant pas SecurityManager “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 du SecurityManager
System.getSecurityManager().getClassContext()[0].getName();
Ou, si elle n’est pas définie, utilisez une classe interne qui l’étend (exemple ci-dessous, copié honteusement du HowTo de Real ):
public static class CurrentClassGetter extends SecurityManager { public Ssortingng getClassName() { return getClassContext()[1].getName(); } }
Si vous voulez le nom complet du paquet, appelez:
Ssortingng name = MyClass.class.getCanonicalName();
Si vous ne voulez que le dernier élément, appelez:
Ssortingng name = MyClass.class.getSimpleName();
L’utilisation MyClass.class.getName()
la classe de l’appelant telle que MyClass.class.getName()
effectivement le travail, mais elle est susceptible de copier / coller des erreurs si vous propagez ce code à de nombreuses classes / sous-classes pour lesquelles vous avez besoin de ce nom de classe.
Et la recette de Tom Hawtin n’est en fait pas mauvaise, il faut juste la préparer correctement 🙂
Si vous avez une classe de base avec une méthode statique qui peut être appelée à partir de sous-classes et que cette méthode statique doit connaître la classe de l’appelant, ceci peut être réalisé comme suit:
class BaseClass { static sharedStaticMethod (Ssortingng 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); } }
Une solution de refactoring sûre, couper et coller qui évite la définition de classes ad hoc ci-dessous.
Écrivez une méthode statique qui récupère le nom de la classe en prenant soin d’inclure le nom de la classe dans le nom de la méthode:
private static Ssortingng getMyClassName(){ return MyClass.class.getName(); }
alors rappelez-le dans votre méthode statique:
public static void myMethod(){ Tracer.debug(getMyClassName(), "message"); }
La sécurité de refactoring est donnée en évitant l’utilisation de chaînes, la sécurité cut & paste est garantie car si vous copiez et collez la méthode caller, vous ne trouverez pas la méthode getMyClassName () dans la classe “MyClass2” cible. Vous devrez donc la redéfinir et la mettre à jour.
Depuis la question Quelque chose comme `this.class` au lieu de` ClassName.class`? est marqué en double pour celui-ci (ce qui est discutable car cette question concerne la classe plutôt que le nom de la classe), je publie 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 cette thisClass
comme privée car:
1) il ne doit pas être hérité: les classes dérivées doivent soit définir leur propre thisClass
ou produire un message d’erreur
2) les références des autres classes doivent être ClassName.class
comme ClassName.class
plutôt que ClassName.thisClass
.
Avec thisClass
défini, l’access au nom de la classe devient:
thisClass.getName()
J’avais besoin du nom de classe dans les méthodes statiques de plusieurs classes, j’ai donc implémenté une classe JavaUtil avec la méthode suivante:
public static Ssortingng getClassName() { Ssortingng className = Thread.currentThread().getStackTrace()[2].getClassName(); int lastIndex = className.lastIndexOf('.'); return className.subssortingng(lastIndex + 1); }
J’espère que ça va aider!
J’ai utilisé ces deux approches pour les scénarios static
et non static
:
Classe principale:
//For non static approach public AndroidLogger(Object classObject) { mClassName = classObject.getClass().getSimpleName(); } //For static approach public AndroidLogger(Ssortingng className) { mClassName = className; }
Comment fournir le nom de la classe:
manière non statique:
private AndroidLogger mLogger = new AndroidLogger(this);
Manière statique:
private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());
Si vous utilisez la reflection, vous pouvez obtenir l’object Method, puis:
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)