515. Java 方法句柄 - 转换 (进阶篇)
除了前面学到的 asType 类型适配和 permuteArguments 参数重排,
MethodHandles 还提供了更强大的 参数插入 和 参数过滤 功能。
这让我们能够像“拼装积木”一样,对方法句柄进行再加工。
1. 插入参数(insertArguments)
有时我们调用方法时,某些参数总是固定的。
与其每次都手动传入,不如把它们提前绑定在 MethodHandle 里。
这就是 insertArguments 的作用:
👉 它允许我们预先绑定一个或多个参数,从而得到一个新的 MethodHandle。
示例:预绑定参数
假设有这样的方法:
public static void test(int v1, String v2, long v3, boolean v4) {
System.out.println(v1 + v2 + v3 + v4);
}
找到方法句柄:
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle target = lookup.findStatic(
Example.class,
"test",
MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class));
我们希望每次调用时,String 和 long 参数总是 "new" 和 3L。
那么可以这样绑定:
MethodHandle bound = MethodHandles.insertArguments(target, 1, "new", 3L);
这里的 1 表示:从第二个参数位置开始绑定。
调用时就只需要传入剩下的参数了:
bound.invoke(1, true);
// 输出: "1new3true"
⚠️ 注意:
如果你再传入已经预绑定的参数,会抛出 WrongMethodTypeException。
2. 过滤参数(filterArguments)
有时我们希望在调用目标方法前,对某些参数做预处理。
这时可以用 filterArguments。
使用方法
filterArguments 需要三个要素:
- 目标方法句柄(最终被调用的方法)。
- 开始过滤的参数索引。
- 一组“过滤器方法句柄”,它们会依次处理指定的参数。
如果某些参数不需要处理,可以传入 null。
甚至可以只处理一部分参数,剩下的保持原样。
示例:参数变换
还是使用刚才的 test 方法。
我们准备两个“过滤器”:
private static boolean negate(boolean original) {
return !original;
}
private static int increment(int original) {
return ++original;
}
分别用于 布尔取反 和 整数自增。
获取它们的方法句柄:
MethodHandle negate = lookup.findStatic(
Example.class, "negate", MethodType.methodType(boolean.class, boolean.class));
MethodHandle increment = lookup.findStatic(
Example.class, "increment", MethodType.methodType(int.class, int.class));
现在组合:
MethodHandle withFilters = MethodHandles.filterArguments(
target, 0, increment, null, null, negate);
解释一下:
-
0表示从第一个参数开始过滤。 - 第一个参数(
int v1) → 用increment过滤。 - 第二个和第三个参数(
String, long) → 不变(传null)。 - 第四个参数(
boolean v4) → 用negate过滤。
调用:
withFilters.invoke(3, "abc", 5L, false);
// 输出: "4abc5true"
👉 我们传入 (3, "abc", 5L, false),
在进入 test 方法前变成了 (4, "abc", 5L, true)。
🎯 总结
-
insertArguments:提前绑定部分参数,适合“固定上下文”。- 类比:在点外卖时,你把“饮料=可乐”“辣度=中辣”写死,下次只选主菜。
-
filterArguments:对传入参数做预处理,适合“数据校正/转换”。- 类比:进入工厂流水线前,先把原材料“切一刀”“翻转一下”,再进入主流程。