Java 方法句柄 : propagate unbound arguments across functions

标签 java methodhandle dynamic-invoke

我想创建一个方法句柄,它允许我将一个值作为参数传递,该值将绑定(bind)到方法句柄树中的占位符。

图,如下所示:f(x) = plus( minus( x, 2), 3) 其中 x 在调用时传递,2 和 3 是始终返回的一些常量 MethodHandles 2 或 3。

我遇到了一个我不明白的问题:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class TTest {

    public static double plus( double a, double b) { return a + b; }
    public static double minus(double a, double b) { return a - b; }

    public static void main(String[] args) throws Throwable {


        MethodHandle mh_minus = MethodHandles.lookup().findStatic(TTest.class, "minus", MethodType.methodType(double.class, double.class, double.class));
        MethodHandle mh_plus =  MethodHandles.lookup().findStatic(TTest.class, "plus",  MethodType.methodType(double.class, double.class, double.class));


        // f(x) = plus( minus( x, 2), 3)
        MethodHandle doubleInvoker = MethodHandles.exactInvoker(MethodType.methodType(double.class));

        // mh_minus takes 2 doubles as input. The second one needs to take a MethodHandle that returns a constant:
        MethodHandle minus_2 = MethodHandles.filterArguments(mh_minus, 1, doubleInvoker);
        minus_2 = MethodHandles.insertArguments(minus_2, 1, MethodHandles.constant(double.class, 2));

        // mh_plus takes 2 doubles as input. The second one needs to take a MethodHandle that returns a constant:
        MethodHandle plus_3 = MethodHandles.filterArguments(mh_plus, 1, doubleInvoker);
        plus_3 = MethodHandles.insertArguments(plus_3, 1, MethodHandles.constant(double.class, 3));

        // the first arg of plus_3 is minus_2, but minus_2 is a MethodHandle that takes a double and returns a double, so we need to filter the first arg of plus_3 by an exact invoker

        // EXCEPTION HERE:
        plus_3 = MethodHandles.filterArguments(plus_3, 0, MethodHandles.exactInvoker(MethodType.methodType(double.class, double.class)));
        MethodHandle comp = plus_3.bindTo(minus_2);
        double res = (double)comp.invokeExact(3.0); // performs (3 - 2) + 3
    }
}

尝试过滤 plus_3 时会返回异常:

Exception in thread "main" java.lang.IllegalArgumentException: target and filter types do not match: (double)double, (MethodHandle,double)double
    at java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:145)
    at java.lang.invoke.MethodHandles.filterArgumentChecks(MethodHandles.java:2631)
    at java.lang.invoke.MethodHandles.filterArgument(MethodHandles.java:2608)
    at java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2601)
    at TTest.main(TTest.java:31)

我不明白的是,如何将加号与减号组合起来,其中减号有一个尚未满足的参数。

你能帮我吗?

最佳答案

对于那些感兴趣的人,这里的技巧是:MethodHandles::collectArguments .

代码现在看起来像这样:

   public static void withCollectArguments() throws Throwable {

        MethodHandle mh_minus      = MethodHandles.lookup().findStatic(
                TTest.class,
                "minus",
                MethodType.methodType(
                        double.class, // output
                        double.class, // arg1
                        double.class  // arg2
                )
        );

        MethodHandle mh_plus       =  MethodHandles.lookup().findStatic(
                TTest.class,
                "plus",
                MethodType.methodType(
                        double.class,
                        double.class,
                        double.class
                )
        );

        // this guys here is used in filters to do a MethodHandle.invoke() -> double conversion
        MethodHandle doubleInvoker = MethodHandles.exactInvoker(
                MethodType.methodType(
                        double.class
                )
        );

        // I want: f(x) = plus( minus( x, 2.0), 3.0) === (x - 2.0 ) + 3.0

        // mh_minus takes 2 doubles as input. The second one needs to
        // take a MethodHandle that returns a constant, so we filter
        MethodHandle x_minus_2 = MethodHandles.filterArguments(
                mh_minus,
                1,
                doubleInvoker
        );

        x_minus_2 = MethodHandles.insertArguments(
                x_minus_2,
                1,
                MethodHandles.constant(double.class, 2)
        );

        // this here is the magic: we collect arguments of mh_plus (double, double),
        // starting at argument index 0,and the collector is minus_2 ( double ) :
        // we will "eat" the first arg of mh_plus and replace it with minus_2
        MethodHandle x_minus_2_plus_y = MethodHandles.collectArguments(
                mh_plus,
                0,
                x_minus_2
        );

        // we then curry x_minus_2_plus_y with a constant as 2nd argument: y => 3
        MethodHandle x_minus_2_plus_3 = MethodHandles.filterArguments(
                x_minus_2_plus_y,
                1,
                doubleInvoker
        );

        // "( x - 2 ) + y" becomes "( x - 2 ) + 3"
        x_minus_2_plus_3 = MethodHandles.insertArguments(
                x_minus_2_plus_3,
                1,
                MethodHandles.constant(
                        double.class,
                        3
                )
        );

        // we now have a method handle that takes 1 argument and dispatches it to minus
        double res = (double)x_minus_2_plus_3.invokeExact(1.0); // performs ( x=1.0 - 2.0) + 3.0
        Assert.assertEquals(res, 2.0);

        res = (double)x_minus_2_plus_3.invokeExact(5.0); // performs ( x=5.0 -2.0 ) + 3.0
        Assert.assertEquals(res, 6.0);
    }

关于Java 方法句柄 : propagate unbound arguments across functions,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37481497/

相关文章:

内存不足时的 Java EE 堆转储

java - 如何在没有本地 XSD 文件的情况下根据 XML 架构验证 XML?

java - 使用 MethodHandle 查找最具体的重载方法

c# - 从 C# 动态调用非托管 VB COM dll 时遇到问题?

java - 如何在 Jackson JSON(反)序列化中使用自定义键类型自定义序列化或转换 Map 属性?

java - JSP 的分页问题

java - LambdaMetafactory 访问不同 ClassLoader 上的类

java - MethodHandles.filterArguments() 示例给出了异常

c# - __DynamicallyInvokable 属性有什么用?

c# - 快速获取 Expression 方法调用目标的方法