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.

205
demandé sur Miles D 2009-06-02 00:42:16

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
203
répondu toolkit 2017-05-23 12:10:54

Faire ce toolkit dit. Ne faites rien de ce genre:

return new Object() { }.getClass().getEnclosingClass();
111
répondu Tom Hawtin - tackline 2009-06-01 20:50:18

en Java 7+ vous pouvez le faire dans la méthode statique / les champs:

MethodHandles.lookup().lookupClass()
65
répondu Rein 2015-04-06 18:33:51

Cette instruction fonctionne très bien:

Thread.currentThread().getStackTrace()[1].getClassName();
37
répondu Keksi 2011-07-15 08:59:17

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

34
répondu lifelongcoug 2011-08-05 21:20:49

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 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:

  1. new Object(){}.getClass().getEnclosingClass();

    noté par Tom Hawtin-tackline

  2. getClassContext()[0].getName(); de la SecurityManager

    noté par Christoffer

  3. new Throwable().getStackTrace()[0].getClassName();

    par comte ludwig

  4. Thread.currentThread().getStackTrace()[1].getClassName();

    de Keksi

  5. et, enfin, génial

    MethodHandles.lookup().lookupClass();

    de Rein



J'ai préparé un 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

  1. meilleure variante à utiliser , plutôt propre et monstrueusement rapide.

    disponible uniquement depuis Java 7 et Android API 26!
 MethodHandles.lookup().lookupClass();
  1. 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();
  1. 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 classe SecurityManager . 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())
 }
  1. vous n'avez probablement jamais besoin d'utiliser les deux dernières variantes basées sur le getStackTrace() d'exception ou le Thread.currentThread() . très inefficace et ne peut retourner que le nom de la classe comme un String , pas le Class<*> 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
}
28
répondu Artyom Krivolapov 2018-01-01 17:38:36

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()); 
21
répondu count ludwig 2011-07-15 07:35:06

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(); 
    }
}
13
répondu Christoffer 2009-06-01 21:03:48

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();
9
répondu James Van Huis 2009-06-01 20:47:50

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);
  }
}
5
répondu Sergey Ushakov 2017-05-23 11:33:26

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.

4
répondu avalori 2014-02-12 14:25:12

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()
3
répondu 18446744073709551615 2017-05-23 11:47:36

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 !

1
répondu phaderer 2015-03-26 10:21:47

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());
0
répondu 0xalihn 2017-12-22 13:55:23

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)
-1
répondu Franzé Jr. 2015-06-11 16:11:30