如何获取Java 8方法引用的MethodInfo?
请看下面的代码:
Method methodInfo = MyClass.class.getMethod("myMethod");
这个工作,但方法名称是作为一个string传递,所以即使myMethod不存在,这将编译。
另一方面,Java 8引入了一个方法引用特性。 在编译时检查。 有可能使用此function获取方法信息?
printMethodName(MyClass::myMethod);
完整的例子:
@FunctionalInterface private interface Action { void invoke(); } private static class MyClass { public static void myMethod() { } } private static void printMethodName(Action action) { } public static void main(String[] args) throws NoSuchMethodException { // This works, but method name is passed as a string, so this will compile // even if myMethod does not exist Method methodInfo = MyClass.class.getMethod("myMethod"); // Here we pass reference to a method. It is somehow possible to // obtain java.lang.reflect.Method for myMethod inside printMethodName? printMethodName(MyClass::myMethod); }
换句话说,我想有一个代码,这是相当于下面的C#代码:
private static class InnerClass { public static void MyMethod() { Console.WriteLine("Hello"); } } static void PrintMethodName(Action action) { // Can I get java.lang.reflect.Method in the same way? MethodInfo methodInfo = action.GetMethodInfo(); } static void Main() { PrintMethodName(InnerClass.MyMethod); }
不,没有可靠的,支持的方式来做到这一点。 您将一个方法引用分配给一个function接口的实例,但该实例由LambdaMetaFactory
,并且无法钻取该实例以查找最初绑定的方法。
Java中的Lambdas和方法引用与C#中的代表完全不同。 对于一些有趣的背景,请阅读invokedynamic
。
在这里的其他答案和评论表明,目前可能有一些额外的工作检索绑定的方法,但要确保你了解的警告。
看起来好像有可能通过使用代理logging哪个方法被调用。
在我的情况下,我正在寻找一种在unit testing中摆脱这种情况的方法:
Point p = getAPoint(); assertEquals(p.getX(), 4, "x"); assertEquals(p.getY(), 6, "x");
正如你可以看到有人正在testing方法getAPoint
并检查坐标是否如预期的一样,但是在每个断言的描述中被复制,并且与被检查的内容不同步。 最好只写一次。
从@ddan的想法,我使用Mockito构build了一个代理解决scheme:
private<T> void assertPropertyEqual(final T object, final Function<T, ?> getter, final Object expected) { final String methodName = getMethodName(object.getClass(), getter); assertEquals(getter.apply(object), expected, methodName); } @SuppressWarnings("unchecked") private<T> String getMethodName(final Class<?> clazz, final Function<T, ?> getter) { final Method[] method = new Method[1]; getter.apply((T)Mockito.mock(clazz, Mockito.withSettings().invocationListeners(methodInvocationReport -> { method[0] = ((InvocationOnMock) methodInvocationReport.getInvocation()).getMethod(); }))); return method[0].getName(); }
不,我可以简单地使用
assertPropertyEqual(p, Point::getX, 4); assertPropertyEqual(p, Point::getY, 6);
并且assert的描述保证与代码同步。
下行:
- 会比上面慢一点
- 需要Mockito工作
- 除了上面的用例之外,对于其他任何东西都不起作
然而,它确实显示了如何做到这一点。
虽然我自己没有尝试过,但我认为答案是“否”,因为方法引用在语义上与lambda相同。
如果你可以使接口Action
扩展Serializable
,那么从另一个问题的答案似乎提供了一个解决scheme(至less在一些编译器和运行时)。
尝试这个
Thread.currentThread().getStackTrace()[2].getMethodName();
我们已经发布了可以用来捕获方法名称的小型库de.cronn:reflection-util 。
例:
class MyClass { public void myMethod() { } } String methodName = ClassUtils.getVoidMethodName(MyClass.class, MyClass::myMethod); System.out.println(methodName); // prints "myMethod"
实现细节:使用ByteBuddy创buildMyClass
的Proxy子类,并捕获方法的调用以检索其名称。 ClassUtils
caching信息,使得我们不需要为每个调用创build一个新的代理。
请注意,这种方法不适用于静态方法。