Czy istnieje sposób na uzyskanie nazwy aktualnie wykonywanej metody w Javie?
Czy istnieje sposób na uzyskanie nazwy aktualnie wykonywanej metody w Javie?
Odpowiedzi:
Thread.currentThread().getStackTrace()
zwykle zawiera metodę, z której ją wywołujesz, ale występują pułapki (patrz Javadoc ):
Niektóre maszyny wirtualne mogą w pewnych okolicznościach pomijać jedną lub więcej ramek stosu ze śledzenia stosu. W skrajnym przypadku maszyna wirtualna, która nie ma informacji o stosie dotyczących tego wątku, może zwrócić tablicę o zerowej długości z tej metody.
Technicznie to zadziała ...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Jednak nowa anonimowa klasa wewnętrzna zostanie utworzona podczas kompilacji (np YourClass$1.class
.). Spowoduje to utworzenie .class
pliku dla każdej metody wdrażającej tę sztuczkę. Dodatkowo przy każdym wywołaniu tworzona jest instancja nieużywanego obiektu w czasie wykonywania. Może to być dopuszczalna sztuczka debugowania, ale wiąże się to ze znacznym nakładem pracy.
Zaletą tej sztuczki jest to, że getEncosingMethod()
zwroty java.lang.reflect.Method
można wykorzystać do pobrania wszystkich innych informacji o metodzie, w tym adnotacji i nazw parametrów. Umożliwia to rozróżnienie konkretnych metod o tej samej nazwie (przeciążenie metody).
Zauważ, że zgodnie z JavaDoc getEnclosingMethod()
tej sztuczki nie należy rzucać, SecurityException
ponieważ klasy wewnętrzne powinny być ładowane przy użyciu tego samego modułu ładującego klasy. Nie ma więc potrzeby sprawdzania warunków dostępu, nawet jeśli obecny jest menedżer bezpieczeństwa.
Wymagane jest użycie getEnclosingConstructor()
dla konstruktorów. Podczas bloków poza (nazwanymi) metodami getEnclosingMethod()
zwraca null
.
getEnclosingMethod
pobiera nazwę metody, w której klasa jest zdefiniowana. this.getClass()
w ogóle ci nie pomoże. @wutzebaer, dlaczego miałbyś to robić? Masz już do nich dostęp.
Styczeń 2009:
Pełny kod będzie (do użycia z zastrzeżeniem @ Bombe ):
/**
* 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
}
Więcej w tym pytaniu .
Aktualizacja z grudnia 2011 r .:
niebieskawy komentarze:
Używam środowiska JRE 6 i podaje mi niepoprawną nazwę metody.
Działa, jeśli piszęste[2 + depth].getMethodName().
0
jestgetStackTrace()
,1
jestgetMethodName(int depth)
i2
wywołuje metodę.
virgo47 „s odpowiedź (upvoted) rzeczywiście oblicza indeks prawo do zastosowania w celu uzyskania z powrotem nazwę metody.
StackTraceElement
tablicę w celu debugowania i sprawdzić, czy 'main' jest rzeczywiście właściwą metodą?
ste[2 + depth].getMethodName()
. 0 oznacza getStackTrace()
, 1 oznacza, getMethodName(int depth)
a 2 wywołuje metodę. Zobacz także odpowiedź @ virgo47 .
Użyliśmy tego kodu, aby ograniczyć potencjalną zmienność indeksu śledzenia stosu - teraz wystarczy wywołać metodę methodName
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();
}
}
Wydaje się, że mamy nadmiar inżynierii, ale mieliśmy pewną stałą liczbę dla JDK 1.5 i byliśmy nieco zaskoczeni, że zmieniło się, kiedy przeszliśmy na JDK 1.6. Teraz jest tak samo w Javie 6/7, ale nigdy nie wiadomo. Nie świadczy to o zmianach tego indeksu w czasie wykonywania - ale mam nadzieję, że HotSpot nie robi tak źle. :-)
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
nazwa będzie miała wartość foo.
null
Obie te opcje działają dla mnie w Javie:
new Object(){}.getClass().getEnclosingMethod().getName()
Lub:
Thread.currentThread().getStackTrace()[1].getMethodName()
Najszybciej odkryłem, że:
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;
}
}
}
Uzyskuje bezpośredni dostęp do natywnej metody getStackTraceElement (int depth). I przechowuje dostępną metodę w zmiennej statycznej.
new Throwable().getStackTrace()
zajęło 5614 ms.
Użyj następującego kodu:
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}
Jest to rozszerzenie odpowiedzi virgo47 (powyżej).
Zapewnia niektóre metody statyczne, aby uzyskać bieżące i wywołujące nazwy klas / metod.
/* Utility class: Getting the name of the current executing method
* /programming/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. /programming/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 + ")";
}
}
Aby uzyskać nazwę metody, która wywołała bieżącą metodę, możesz użyć:
new Exception("is not thrown").getStackTrace()[1].getMethodName()
Działa to na moim MacBooku, a także na telefonie z Androidem
Próbowałem także:
Thread.currentThread().getStackTrace()[1]
ale Android zwróci „getStackTrace”. Mogę to naprawić dla Androida
Thread.currentThread().getStackTrace()[2]
ale potem otrzymuję złą odpowiedź na moim MacBooku
getStackTrace()[0]
, niż getStackTrace()[1]
. YMMV.
Thread.currentThread().getStackTrace()[2]
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
}
}
final StackTraceElement e = Thread.currentThread().getStackTrace()[2];
Pracuje; e.getClassName();
zwraca pełną nazwę klasy i e.getMethodName()
zwraca nazwę methon.
getStackTrace()[2]
jest źle, musi być tak, getStackTrace()[3]
ponieważ: [0] dalvik.system.VMStack.getThreadStackTrace [1] java.lang.Thread.getStackTrace [2] Utils.getCurrentClassAndMethodNames [3] Funkcja a () wywołująca tę
Można to zrobić za pomocą StackWalker
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
jest zaprojektowany tak, aby był leniwy, więc może być bardziej wydajny niż, powiedzmy, Thread.getStackTrace
który chętnie tworzy tablicę dla całego zestawu wywołań. Zobacz także JEP, aby uzyskać więcej informacji.
Alternatywną metodą jest utworzenie, ale nie zgłoszenie wyjątku, i użycie tego obiektu, z którego można pobrać dane śledzenia stosu, ponieważ metoda zamykająca zwykle ma indeks 0 - o ile JVM przechowuje te informacje, tak jak inni wspomniano powyżej. Nie jest to jednak najtańsza metoda.
Od Throwable.getStackTrace () (było tak samo od co najmniej Java 5):
Element zerowy tablicy (przy założeniu, że długość tablicy jest różna od zera) reprezentuje górę stosu, która jest ostatnim wywołaniem metody w sekwencji. Zazwyczaj jest to punkt, w którym ten rzucalny został stworzony i rzucony.
Poniższy fragment zakłada, że klasa jest niestatyczna (z powodu getClass ()), ale to na bok.
System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());
String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
Mam rozwiązanie korzystające z tego (w Androidzie)
/**
* @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())}));
}
}
Nie wiem, jaki jest zamiar uzyskania nazwy obecnie wykonywanej metody, ale jeśli jest to tylko w celu debugowania, struktury rejestrowania takie jak „logback” mogą tutaj pomóc. Na przykład w logback wszystko, co musisz zrobić, to użyć wzorca „% M” w konfiguracji logowania . Należy jednak zachować ostrożność, ponieważ może to obniżyć wydajność.
Na wypadek, gdyby metoda, którą chcesz znać, jest metodą testową junit, możesz użyć reguły junit TestName: https://stackoverflow.com/a/1426730/3076107
Większość odpowiedzi tutaj wydaje się błędna.
public static String getCurrentMethod() {
return getCurrentMethod(1);
}
public static String getCurrentMethod(int skip) {
return Thread.currentThread().getStackTrace()[1 + 1 + skip].getMethodName();
}
Przykład:
public static void main(String[] args) {
aaa();
}
public static void aaa() {
System.out.println("aaa -> " + getCurrentMethod( ) );
System.out.println("aaa -> " + getCurrentMethod(0) );
System.out.println("main -> " + getCurrentMethod(1) );
}
Wyjścia:
aaa -> aaa
aaa -> aaa
main -> main
Przepisałem trochę odpowiedź maklemenz :
private static Method m;
static {
try {
m = Throwable.class.getDeclaredMethod(
"getStackTraceElement",
int.class
);
}
catch (final NoSuchMethodException e) {
throw new NoSuchMethodUncheckedException(e);
}
catch (final SecurityException e) {
throw new SecurityUncheckedException(e);
}
}
public static String getMethodName(int depth) {
StackTraceElement element;
final boolean accessible = m.isAccessible();
m.setAccessible(true);
try {
element = (StackTraceElement) m.invoke(new Throwable(), 1 + depth);
}
catch (final IllegalAccessException e) {
throw new IllegalAccessUncheckedException(e);
}
catch (final InvocationTargetException e) {
throw new InvocationTargetUncheckedException(e);
}
finally {
m.setAccessible(accessible);
}
return element.getMethodName();
}
public static String getMethodName() {
return getMethodName(1);
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
getEnclosingMethod()
rzuca NullPointerException
dla mnie w Javie 7.
Co jest złego w tym podejściu:
class Example {
FileOutputStream fileOutputStream;
public Example() {
//System.out.println("Example.Example()");
debug("Example.Example()",false); // toggle
try {
fileOutputStream = new FileOutputStream("debug.txt");
} catch (Exception exception) {
debug(exception + Calendar.getInstance().getTime());
}
}
private boolean was911AnInsideJob() {
System.out.println("Example.was911AnInsideJob()");
return true;
}
public boolean shouldGWBushBeImpeached(){
System.out.println("Example.shouldGWBushBeImpeached()");
return true;
}
public void setPunishment(int yearsInJail){
debug("Server.setPunishment(int yearsInJail=" + yearsInJail + ")",true);
}
}
I zanim ludzie oszaleją na temat używania System.out.println(...)
, zawsze możesz i powinieneś stworzyć jakąś metodę, aby dane wyjściowe mogły zostać przekierowane, np .:
private void debug (Object object) {
debug(object,true);
}
private void dedub(Object object, boolean debug) {
if (debug) {
System.out.println(object);
// you can also write to a file but make sure the output stream
// ISN'T opened every time debug(Object object) is called
fileOutputStream.write(object.toString().getBytes());
}
}