web-dev-qa-db-fra.com

Obtenir le nom de la méthode en cours d'exécution

Est-il possible d'obtenir le nom de la méthode en cours d'exécution en Java?

438
Omar Kooheji

Thread.currentThread().getStackTrace() contient généralement la méthode à partir de laquelle vous appelez, mais il existe des pièges (voir Javadoc ):

Certaines machines virtuelles peuvent, dans certaines circonstances, omettre un ou plusieurs cadres de pile de la trace de pile. Dans les cas extrêmes, une machine virtuelle ne disposant d'aucune information de trace de pile concernant ce thread est autorisée à renvoyer un tableau de longueur nulle à partir de cette méthode.

168
Bombe

Techniquement, ça va marcher ...

String name = new Object(){}.getClass().getEnclosingMethod().getName();

Cependant, une nouvelle classe interne anonyme sera créée pendant la compilation (par exemple, YourClass$1.class). Donc, cela va créer un fichier .class pour chaque méthode qui déploie cette astuce. En outre, une instance d'objet inutilisée est créée à chaque appel lors de l'exécution. Donc, cela peut être une astuce de débogage acceptable, mais cela entraîne des frais généraux importants.

Un avantage de cette astuce est que getEncosingMethod() renvoie Java.lang.reflect.Method qui peut être utilisé pour extraire toutes les autres informations de la méthode, y compris les annotations et les noms de paramètres. Cela permet de distinguer des méthodes spécifiques portant le même nom (surcharge de méthode).

Notez que selon le JavaDoc de getEnclosingMethod(), cette astuce ne devrait pas générer de SecurityException, car les classes internes doivent être chargées à l'aide du même chargeur de classes. Il n'est donc pas nécessaire de vérifier les conditions d'accès même en présence d'un responsable de la sécurité.

Il est nécessaire d'utiliser getEnclosingConstructor() pour les constructeurs. Pendant les blocs en dehors des méthodes (nommées), getEnclosingMethod() renvoie null.

293
Devin

Janvier 2009:
Un code complet serait (à utiliser avec mise en garde de @ Bombe à l'esprit):

/**
 * Get the method name for a depth in call stack. <br />
 * Utility function
 * @param depth depth in the call stack (0 means current method, 1 means call method, ...)
 * @return method name
 */
public static String getMethodName(final int depth)
{
  final StackTraceElement[] ste = Thread.currentThread().getStackTrace();

  //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
  // return ste[ste.length - depth].getMethodName();  //Wrong, fails for depth = 0
  return ste[ste.length - 1 - depth].getMethodName(); //Thank you Tom Tresansky
}

Plus dans cette question .

Mise à jour de décembre 2011:

bleuâtre commentaires:

J'utilise JRE 6 et me donne un nom de méthode incorrect.
Cela fonctionne si j'écris ste[2 + depth].getMethodName().

  • 0 est getStackTrace(),
  • 1 est getMethodName(int depth) et
  • 2 appelle la méthode.

virgo47 's answer (upvoted) calcule en fait le bon index à appliquer pour récupérer le nom de la méthode.

134
VonC

Nous avons utilisé ce code pour atténuer la variabilité potentielle de l'index de trace de pile. Appelez maintenant methodName util:

public class MethodNameTest {
    private static final int CLIENT_CODE_STACK_INDEX;

    static {
        // Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
        int i = 0;
        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
            i++;
            if (ste.getClassName().equals(MethodNameTest.class.getName())) {
                break;
            }
        }
        CLIENT_CODE_STACK_INDEX = i;
    }

    public static void main(String[] args) {
        System.out.println("methodName() = " + methodName());
        System.out.println("CLIENT_CODE_STACK_INDEX = " + CLIENT_CODE_STACK_INDEX);
    }

    public static String methodName() {
        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX].getMethodName();
    }
}

Cela semble excessif, mais nous avions un numéro fixe pour JDK 1.5 et nous étions un peu surpris que cela change lorsque nous sommes passés à JDK 1.6. Maintenant, c'est la même chose dans Java 6/7, mais on ne sait jamais. Il ne s'agit pas d'une preuve des modifications apportées à cet index lors de l'exécution - mais heureusement, HotSpot n'en fait pas autant. :-)

82
virgo47
 public class SomeClass {
   public void foo(){
      class Local {};
      String name = Local.class.getEnclosingMethod().getName();
   }
 }

nom aura une valeur foo.

44
alexsmail

Le moyen le plus rapide J'ai trouvé est que:

import Java.lang.reflect.Method;

public class TraceHelper {
    // save it static to have it available on every call
    private static Method m;

    static {
        try {
            m = Throwable.class.getDeclaredMethod("getStackTraceElement",
                    int.class);
            m.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getMethodName(final int depth) {
        try {
            StackTraceElement element = (StackTraceElement) m.invoke(
                    new Throwable(), depth + 1);
            return element.getMethodName();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Il accède directement à la méthode native getStackTraceElement (int depth). Et stocke la méthode accessible dans une variable statique.

31
maklemenz

Ces deux options fonctionnent pour moi avec Java:

new Object(){}.getClass().getEnclosingMethod().getName()

Ou:

Thread.currentThread().getStackTrace()[1].getMethodName()
31
Charlie Seligman

Utilisez le code suivant:

    StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
    StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
    String methodName = e.getMethodName();
    System.out.println(methodName);
25
Sumit Singh
public static String getCurrentMethodName() {
        return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
    }
15
Jay

Ceci est une extension de réponse de virgo47 (ci-dessus).

Il fournit des méthodes statiques pour obtenir les noms de classe/méthode actuels et appelants.

/* Utility class: Getting the name of the current executing method 
 * https://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method
 * 
 * Provides: 
 * 
 *      getCurrentClassName()
 *      getCurrentMethodName()
 *      getCurrentFileName()
 * 
 *      getInvokingClassName()
 *      getInvokingMethodName()
 *      getInvokingFileName()
 *
 * Nb. Using StackTrace's to get this info is expensive. There are more optimised ways to obtain
 * method names. See other stackoverflow posts eg. https://stackoverflow.com/questions/421280/in-Java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection/2924426#2924426
 *
 * 29/09/2012 (lem) - added methods to return (1) fully qualified names and (2) invoking class/method names
 */
package com.stackoverflow.util;

public class StackTraceInfo
{
    /* (Lifted from virgo47's stackoverflow answer) */
    private static final int CLIENT_CODE_STACK_INDEX;

    static {
        // Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
        int i = 0;
        for (StackTraceElement ste: Thread.currentThread().getStackTrace())
        {
            i++;
            if (ste.getClassName().equals(StackTraceInfo.class.getName()))
            {
                break;
            }
        }
        CLIENT_CODE_STACK_INDEX = i;
    }

    public static String getCurrentMethodName()
    {
        return getCurrentMethodName(1);     // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentMethodName(int offset)
    {
        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getMethodName();
    }

    public static String getCurrentClassName()
    {
        return getCurrentClassName(1);      // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentClassName(int offset)
    {
    return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getClassName();
    }

    public static String getCurrentFileName()
    {
        return getCurrentFileName(1);     // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentFileName(int offset)
    {
        String filename = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getFileName();
        int lineNumber = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getLineNumber();

        return filename + ":" + lineNumber;
    }

    public static String getInvokingMethodName()
    {
        return getInvokingMethodName(2); 
    }

    private static String getInvokingMethodName(int offset)
    {
        return getCurrentMethodName(offset + 1);    // re-uses getCurrentMethodName() with desired index
    }

    public static String getInvokingClassName()
    {
        return getInvokingClassName(2); 
    }

    private static String getInvokingClassName(int offset)
    {
        return getCurrentClassName(offset + 1);     // re-uses getCurrentClassName() with desired index
    }

    public static String getInvokingFileName()
    {
        return getInvokingFileName(2); 
    }

    private static String getInvokingFileName(int offset)
    {
        return getCurrentFileName(offset + 1);     // re-uses getCurrentFileName() with desired index
    }

    public static String getCurrentMethodNameFqn()
    {
        return getCurrentMethodNameFqn(1);
    }

    private static String getCurrentMethodNameFqn(int offset)
    {
        String currentClassName = getCurrentClassName(offset + 1);
        String currentMethodName = getCurrentMethodName(offset + 1);

        return currentClassName + "." + currentMethodName ;
    }

    public static String getCurrentFileNameFqn()
    {
        String CurrentMethodNameFqn = getCurrentMethodNameFqn(1);
        String currentFileName = getCurrentFileName(1);

        return CurrentMethodNameFqn + "(" + currentFileName + ")";
    }

    public static String getInvokingMethodNameFqn()
    {
        return getInvokingMethodNameFqn(2);
    }

    private static String getInvokingMethodNameFqn(int offset)
    {
        String invokingClassName = getInvokingClassName(offset + 1);
        String invokingMethodName = getInvokingMethodName(offset + 1);

        return invokingClassName + "." + invokingMethodName;
    }

    public static String getInvokingFileNameFqn()
    {
        String invokingMethodNameFqn = getInvokingMethodNameFqn(2);
        String invokingFileName = getInvokingFileName(2);

        return invokingMethodNameFqn + "(" + invokingFileName + ")";
    }
}
13
mvanle

Pour obtenir le nom de la méthode qui a appelé la méthode actuelle, vous pouvez utiliser:

new Exception("is not thrown").getStackTrace()[1].getMethodName()

Cela fonctionne aussi bien sur mon MacBook que sur mon téléphone Android.

J'ai aussi essayé:

Thread.currentThread().getStackTrace()[1]

mais Android renverra "getStackTrace" Je pourrais résoudre ce problème pour Android avec

Thread.currentThread().getStackTrace()[2]

mais alors je reçois la mauvaise réponse sur mon MacBook

12
Friso van der Made

Util.Java:

public static String getCurrentClassAndMethodNames() {
    final StackTraceElement e = Thread.currentThread().getStackTrace()[2];
    final String s = e.getClassName();
    return s.substring(s.lastIndexOf('.') + 1, s.length()) + "." + e.getMethodName();
}

SomeClass.Java:

public class SomeClass {
    public static void main(String[] args) {
        System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
    }
}
10
Maxple

Cela peut être fait en utilisant StackWalker depuis Java 9.

public static String getCurrentMethodName() {
    return StackWalker.getInstance()
                      .walk(s -> s.skip(1).findFirst())
                      .get()
                      .getMethodName();
}

public static String getCallerMethodName() {
    return StackWalker.getInstance()
                      .walk(s -> s.skip(2).findFirst())
                      .get()
                      .getMethodName();
}

StackWalker est conçu pour être paresseux, il est donc probablement plus efficace que, par exemple, Thread.getStackTrace, qui crée avec impatience un tableau pour l'ensemble de la pile d'appels. Voir également le PEC pour plus d'informations.

5
Radiodef
String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
4
ran

Une autre méthode consiste à créer une exception, mais pas à la lancer, et à utiliser cet objet à partir duquel obtenir les données de trace de la pile, car la méthode englobante aura généralement être à l'indice 0 - tant que la machine virtuelle Java stocke ces informations, comme d'autres l'ont mentionné plus haut. Ce n'est pas la méthode la moins chère, cependant.

De Throwable.getStackTrace () (identique depuis Java 5 au moins):

L'élément zeroth du tableau (en supposant que la longueur du tableau est non nulle) représente le haut de la pile, qui correspond à la dernière invocation de méthode de la séquence. Typiquement , il s'agit du point où ce jetable a été créé et jeté.

L'extrait ci-dessous suppose que la classe est non statique (à cause de getClass ()), mais c'est un aparté.

System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());
4
Jool

J'ai une solution en utilisant ceci (dans Android)

/**
 * @param className       fully qualified className
 *                        <br/>
 *                        <code>YourClassName.class.getName();</code>
 *                        <br/><br/>
 * @param classSimpleName simpleClassName
 *                        <br/>
 *                        <code>YourClassName.class.getSimpleName();</code>
 *                        <br/><br/>
 */
public static void getStackTrace(final String className, final String classSimpleName) {
    final StackTraceElement[] steArray = Thread.currentThread().getStackTrace();
    int index = 0;
    for (StackTraceElement ste : steArray) {
        if (ste.getClassName().equals(className)) {
            break;
        }
        index++;
    }
    if (index >= steArray.length) {
        // Little Hacky
        Log.w(classSimpleName, Arrays.toString(new String[]{steArray[3].getMethodName(), String.valueOf(steArray[3].getLineNumber())}));
    } else {
        // Legitimate
        Log.w(classSimpleName, Arrays.toString(new String[]{steArray[index].getMethodName(), String.valueOf(steArray[index].getLineNumber())}));
    }
}
3
Kasim Rangwala

Je ne sais pas quelle est l'intention de récupérer le nom de la méthode actuellement exécutée, mais si cela sert uniquement à des fins de débogage, alors des frameworks de journalisation comme "logback" peuvent aider ici. Par exemple, dans la consignation, tout ce que vous avez à faire est de tilisez le modèle "% M" dans votre configuration de journalisation . Toutefois, cela doit être utilisé avec prudence, car cela pourrait dégrader les performances.

3
James Selvakumar

Juste au cas où la méthode dont vous voulez connaître le nom est une méthode de test Junit, vous pouvez utiliser la règle junit TestName: https://stackoverflow.com/a/1426730/3076107

2
Egl
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
1
Darren