Java 方法句柄:跨函数传播未绑定参数
Java Method Handles: propagate unbound arguments across functions
我想创建一个方法句柄,允许我将一个值作为参数传递,该值将绑定到方法句柄树下的占位符。
图,像这样:f(x) = plus( minus( x, 2), 3)
其中 x 在调用时传递,2 和 3 是一些常量 MethodHandles return 总是 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
}
}
这 return 在尝试过滤时出现异常 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)
我不明白的是,我如何将 plus 与 minus 组合在一起,其中 minus 有一个参数尚未填满。
你能帮帮我吗?
对于那些感兴趣的人,这里的技巧是: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);
}
我想创建一个方法句柄,允许我将一个值作为参数传递,该值将绑定到方法句柄树下的占位符。
图,像这样:f(x) = plus( minus( x, 2), 3)
其中 x 在调用时传递,2 和 3 是一些常量 MethodHandles return 总是 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
}
}
这 return 在尝试过滤时出现异常 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)
我不明白的是,我如何将 plus 与 minus 组合在一起,其中 minus 有一个参数尚未填满。
你能帮帮我吗?
对于那些感兴趣的人,这里的技巧是: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);
}