如何获取 Java 8 方法参考的方法信息?

2022-08-31 11:11:27

请看下面的代码:

Method methodInfo = MyClass.class.getMethod("myMethod");

这有效,但方法名称作为字符串传递,因此即使myMethod不存在,这也将编译。

另一方面,Java 8 引入了一个方法参考功能。它在编译时进行检查。是否可以使用此功能来获取方法信息?

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);
    }

答案 1

不,没有可靠的,受支持的方法来执行此操作。您将方法引用分配给功能接口的实例,但该实例由 组成,并且无法钻取它以查找最初绑定到的方法。LambdaMetaFactory

Java 中的 Lambda 和方法引用的工作方式与 C# 中的委托完全不同。有关一些有趣的背景,请阅读.invokedynamic

此处的其他答案和注释表明,目前可以通过一些额外的工作来检索绑定方法,但请确保您了解注意事项。


答案 2

在我的情况下,我正在寻找一种方法来摆脱单元测试中的这个问题:

Point p = getAPoint();
assertEquals(p.getX(), 4, "x");
assertEquals(p.getY(), 6, "x");

如您所见,有人正在测试方法并检查坐标是否符合预期,但在每个断言的描述中都被复制了,并且与检查的内容不同步。最好只写一次。getAPoint

根据@ddan的想法,我使用Mockito构建了一个代理解决方案:

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);

并且断言的描述保证与代码同步。

缺点:

  • 会比上面稍微慢一点
  • 需要莫基托工作
  • 除了上面的用例之外,对任何东西都没有用。

然而,它确实展示了如何做到这一点。


推荐