【问题标题】:Is it possible to convert method reference to MethodHandle?是否可以将方法引用转换为 MethodHandle?
【发布时间】:2014-10-30 09:34:56
【问题描述】:

是否可以将方法引用(例如SomeClass::someMethod)转换为MethodHandle 实例?我想要编译时检查的好处(确保类和方法存在)以及使用MethodHandle API 内省方法的能力。

用例:当且仅当请求不是由特定方法触发时(以避免无休止的递归),我有需要执行的代码。我想要一个编译时检查来确保类/方法存在,但是运行时检查来比较调用者和方法。

回顾一下:是否可以将方法引用转换为MethodHandle

【问题讨论】:

标签: java java-8 method-reference methodhandle


【解决方案1】:

好吧,如果您能承受额外的开销和安全隐患,您可以使用Serializable 函数式interface 并解码方法引用实例的序列化形式,以找到this answer 中演示的目标或提出再次使用this question and its answers

但是,您真的应该重新考虑您的软件设计。 “避免无休止的递归”不应该通过解码某种参数对象来解决,尤其是如果你的假设是,这个实际参数值代表你的方法的调用者。您将如何执行这种奇怪的关系?

即使是简单的代码更改(例如引用委托给另一个方法的方法)也会破坏您的检查。这是一个简单的示例,显示了您的方法存在的细微问题:

public class SimpleTest {
    public static void main(String... arg) {
        run(SimpleTest::process);
    }
    static void run(BiConsumer<Object,Object> c) {
        c.accept("foo", "bar");
    }
    static void process(Object... arg) {
        Thread.dumpStack();
    }
}

当运行这个程序时,它会打印如下内容:

java.lang.Exception: Stack trace
    at java.lang.Thread.dumpStack(Thread.java:1329)
    at SimpleTest.process(SimpleTest.java:16)
    at SimpleTest.lambda$MR$main$process$a9318f35$1(SimpleTest.java:10)
    at SimpleTest$$Lambda$1/26852690.accept(Unknown Source)
    at SimpleTest.run(SimpleTest.java:13)
    at SimpleTest.main(SimpleTest.java:10)

显示生成实例中的方法引用不是预期的SimpleTest::process,而是SimpleTest::lambda$MR$main$process$a9318f35$1,最终将调用process。原因是某些操作(这里是 varargs 处理)不是由生成的interface 实例执行的,而是由合成方法执行的,就像您编写的run((a,b)-&gt; SimpleTest.process(a,b)) 一样。唯一的区别是合成方法的名称。

您不应该依靠这种脆弱的内省来设计软件。如果你想避免递归,一个简单的ThreadLocal 标志告诉你是否已经在你的特定方法中就可以了。但可能值得问问自己,为什么你的 API 一开始就会引发无休止的递归;似乎有什么根本上的错误……

【讨论】:

  • 为了简化问题,我简化了我的用例。我有一个将请求 URL 映射到处理程序类/方法的 HTTP 服务器。我需要重定向所有未映射到特定处理程序(方法)的传入呼叫。我不想与 URL 进行比较(它会随着时间而改变),我想与一个将被代码重构工具拾取并提供编译时安全性的值进行比较。我还有其他方法可以实现我的目标吗?也就是说,构造一个在编译时验证的方法引用,并在运行时将其与第二个java.lang.reflect.Method 进行比较?
  • 好吧,我假设所有处理程序都实现相同的interface,这也是您通过方法引用实现的功能接口。在这种情况下,我建议实现一个特定的处理程序,您需要将其识别为可以具有明确定义的标识或相等性的普通接口实现。而其他通过方法引用实现的,没有定义的标识或相等性,它们永远不会等于您特定的普通接口实现。
  • 实际上没有,方法没有被接口标记。我使用的是 Jersey (JAX-RS),所以在我的情况下,方法由注释标记(例如 @GET@Path 等)。我想不出一种方法来通过编译时检查来绑定这些方法,以确保我不会留下对不存在方法的悬空引用。
  • 我仍然不清楚 Method 和编译时检查的工件之间的期望比较在哪里起作用。但无论如何,它已经发展成为一个完全不同的问题。所以我建议打开一个新问题,更详细地解释你想要实现的目标(可能有代码示例)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 2022-10-22
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
相关资源
最近更新 更多