如何在Java中获取方法的字符串名称?

35

如何通过反射找到方法的字符串名称?

例如给定:

class Car{
   public void getFoo(){
   }
}

我想要获取字符串"getFoo",类似于以下内容:

 Car.getFoo.toString() == "getFoo" // TRUE
7个回答

41

你可以这样获得字符串:

Car.class.getDeclaredMethods()[0].getName();

这适用于您类中的单个方法情况。如果您想遍历所有已声明的方法,则需要迭代Car.class.getDeclaredMethods()返回的数组:

for (Method method : Car.class.getDeclaredMethods()) {
    String name = method.getName();
}

如果你想查看所有方法,应该使用getDeclaredMethods(),而getMethods()仅返回公共方法。

最后,如果你想要查看当前正在执行的方法名,你应该使用这段代码:

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

这将获取当前线程的堆栈跟踪并返回其顶部方法的名称。


我不明白,你想要获取方法的名称,但是你打算如何指定这个方法呢? - Malcolm
7
你可以通过执行Thread.currentThread().getStackTrace()[0].getMethodName()来获取调用栈中的第一个方法,此时得到的是"getStackTrace"方法。如果你想要获取代码行的方法,应该执行Thread.currentThread().getStackTrace()[1].getMethodName()。如果你想将此方法解耦,以便不必在每个地方都重复使用它,我建议你将它定义为一个静态方法并使用[2]来调用它。虽然我还没有尝试过,但这似乎是可行的逻辑。 - Lau Llobet
不要使用Thread.currentThread().getStackTrace()[0].getMethodName();,我用Thread.currentThread().getStackTrace()[1].getMethodName(); 更好运气 :) 这应该每次都有效,尽管我还没有在构造函数或延迟加载情况下尝试过。 - Alexander Mills
2
在Android上,方法名称位于第三个元素中:Thread.currentThread().getStackTrace()[2].getMethodName()。 - Godfrey Duke
谢谢,这个完美地工作了 Thread.currentThread().getStackTrace()[1].getMethodName(); - Vinayak Dornala
显示剩余4条评论

24

由于方法本身不是对象,因此它们没有直接属性(就像您在诸如JavaScript之类的语言中期望的一等函数一样)。

您能做的最接近的方法是调用Car.class.getMethods()

Car.class是一个Class对象,您可以使用它来调用任何反射方法。

然而,据我所知,一个方法无法识别自己。


1
使用Java 9或更高版本,我们现在可以使用StackWalker来查找实际的类和方法名称(我知道,晚了12年)。 - user16320675

15

所以,您想获取当前正在执行的方法的名称?这里有一种相当丑陋的方法:

Exception e = new Exception();
e.fillInStackTrace();
String methodName = e.getStackTrace()[0].getMethodName();

1
实际上,this.getClass().getEnclosingMethod() 很好用! - mjs
类 Test { @Test public void testReflection() { assert (this.getClass().getEnclosingMethod() == null); } }翻译: momomo,不行,这样做是不可以的。 - Hazel T

9

请查看此线程:

获取当前执行方法的名称

它提供了一些其他的解决方案 - 例如:

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

4

使用Java 8,您可以仅用几行代码(几乎)不需要任何其他库来完成此操作。关键是将您的方法转换为可序列化的lambda表达式。因此,您只需定义一个简单的接口,如下所示:

@FunctionalInterface
public interface SerializableFunction<I, O> extends Function<I, O>, Serializable {
  // Combined interface for Function and Serializable
}

现在,我们需要将lambda表达式转换成SerializedLambda对象。显然,Oracle并不希望我们这样做,所以要谨慎对待...由于所需方法是私有的,因此我们需要使用反射来调用它:

private static final <T> String nameOf(SerializableFunction<T, ?> lambda) {
  Method findMethod = ReflectionUtils.findMethod(lambda.getClass(), "writeReplace");
  findMethod.setAccessible(true);

  SerializedLambda invokeMethod = (SerializedLambda) ReflectionUtils.invokeMethod(findMethod, lambda);
  return invokeMethod.getImplMethodName();
}

我在这里使用Spring的ReflectionUtils类只是为了简单起见,但您当然可以通过手动循环遍历所有超类并使用getDeclaredMethod查找writeReplace方法来替换它。
仅此而已,现在您可以像这样使用它:
@Test
public void testNameOf() throws Throwable {
  assertEquals("getName", nameOf(MyClassTest::getName));
}

我还没有使用Java 9的模块系统检查过这个问题,所以需要说明的是,在更近期的Java版本中可能会更加棘手...


在实现“ReflectionUtils.findMethod”时,请确保使用“getDeclaredMethod('writeReplace')”或“getDeclaredMethods()”,而不是“getMethod('writeReplace')”或“getMethods()”。 - James Wasson

1

试试这个,

 import java.lang.reflect.*;
    public class DumpMethods {
        public static void main(String args[]) {
            try {
                Class c = Class.forName(args[0]);
                Method m[] = c.getDeclaredMethods();
                for (int i = 0; i < m.length; i++)
                    System.out.println(m[i].toString());
            } catch (Throwable e) {
                System.err.println(e);
            }
        }
    }

0
等等,既然你已经知道方法名了,那么你不能直接将其作为字符串输入吗?
不用(伪)Class.methodName.toString(),直接使用"methodName"即可。
否则,你可以使用Class#getDeclaredMethods()来获取类中的所有方法。

14
我不想使用一个字符串。将来可能会重构我的方法名称。 - Andriy Drozdyuk
2
IDE具有在重构时查看字符串的能力。 - Bozho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接