将方法引用转换为MethodHandle是否可行?

12
是否可以将方法引用(例如SomeClass::someMethod)转换为MethodHandle实例?我想要编译时检查的好处(确保类和方法存在),以及使用MethodHandle API内省方法的能力。
使用情况:如果请求没有被特定方法触发(避免无限递归),则需要执行代码。我希望在编译时检查以确保类/方法存在,但在运行时检查调用者是否与该方法相同。
因此,简要回顾一下:是否可以将方法引用转换为MethodHandle

2
https://dev59.com/pmQn5IYBdhLWcg3wCzek - assylias
1
不,你不能;推荐观看:这里。简而言之:方法引用等使用invokedynamic调用站点处理。这样的调用站点是可编程的,参见这里 - fge
1个回答

4
如果您可以承受额外的开销和安全影响,可以使用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。原因是某些操作(此处为可变参数处理)不是由生成的接口实例执行,而是由一个合成方法执行,就像你写了 run((a,b)-> SimpleTest.process(a,b)) 一样。唯一的区别是合成方法的名称。
不应该设计依赖于这种脆弱的内省的软件。如果您想避免递归,简单的 ThreadLocal 标志告诉您是否已经在特定方法中即可完成工作。但是,值得问问自己为什么您的 API 首先会引发无限递归;似乎有一些根本性的问题...

我为了简化问题而简化了我的用例。我有一个将请求URL映射到处理程序类/方法的HTTP服务器。我需要重定向所有未映射到特定处理程序(方法)的传入调用。与其与URL进行比较(随时间变化),我想比较一个能够被代码重构工具捕捉并提供编译时安全性的值。是否有其他方法可以实现我的目标?也就是说,构建一个在编译时已经验证过的方法引用,并将其与第二个java.lang.reflect.Method在运行时进行比较? - Gili
1
我假设所有的处理程序都实现了相同的“接口”,这也是您通过方法引用实现的函数式接口。在这种情况下,我建议实现一个特定的处理程序,您需要将其识别为普通接口实现,该实现可以具有明确定义的标识或相等性。而其他通过方法引用实现的处理程序没有定义的标识或相等性,它们永远不会等于您特定的普通接口实现。 - Holger
不,这些方法并没有被接口标记。我使用Jersey(JAX-RS),所以在我的情况下,方法是通过注释进行标记的(例如“@GET”,“@Path”等)。我想不出有什么方法可以绑定到这些方法,并通过编译时检查来确保我没有留下对不存在方法的悬空引用。 - Gili
1
我仍然不清楚Method和那个在编译时检查的工件之间所需的比较是如何发挥作用的。但无论如何,它已经演变成一个完全不同的问题了。因此,我建议您开一个新的问题,并详细解释您想要实现的内容(可以附带代码示例)。 - Holger

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