517. Java 方法句柄 - 方法句柄 vs 反射 API
1. 背景介绍
- Reflection API :早在 JDK1.1 就引入,允许在运行时动态获取类、方法、字段等元信息,并进行调用。它最强的地方是 类成员的"枚举式探索"(比如获取所有方法、字段),这点是 Method Handles 做不到的。
- Method Handles :JDK7 引入,最初是为了给编译器和语言运行时开发者提供更高效的工具。它不是用来替代 Reflection,而是作为一种 高性能的"方法指针"机制。Method Handles 在调用性能上接近直接调用,但也增加了更灵活的"函数式变换能力"。
2. 访问检查与性能差异
| 特性 | Reflection API | Method Handles |
|---|---|---|
| 访问检查 | 每次调用都会做一次权限检查(例如 setAccessible(true) 之后仍要验证调用上下文) |
只在 创建 MethodHandle 时检查一次,后续调用无需再次检查 |
| 性能 | 慢(额外安全检查) | 快(接近普通方法调用) |
| 风险 | 相对安全,控制粒度细 | 一旦 MethodHandle 被错误地传出去,可能导致 非公开成员暴露,需要开发者谨慎 |
👉 培训提示:可以用"机场安检"做类比:
- Reflection 每次过闸机都要安检。
- MethodHandle 只在第一次进入机场时安检,之后在机场里可以自由走动。
3. 功能对比
- Reflection API :
- 可以枚举和分析类结构(字段、方法、构造器、注解等)。
- 适合用于 框架扫描、元数据处理,比如 Spring 在启动时扫描 Bean 定义。
- Method Handles :
- 提供 高性能调用能力。
- 支持 参数重排、预绑定参数、返回值过滤 等函数式编程风格的操作。
- 不支持列出类的所有成员。
4. 两者的互操作(Conversion)
在实际开发中,Reflection 和 Method Handles 并不是对立的,而是可以互相转换:
4.1 从 Reflection 到 MethodHandle
java
// 通过反射获取 Method
Method method = Example.class.getMethod("test", String.class);
// 转换为 MethodHandle
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.unreflect(method);
// 调用
String result = (String) methodHandle.invoke("something");
4.2 字段的 Getter 和 Setter
java
Field field = Example.class.getField("magic");
MethodHandle setter = lookup.unreflectSetter(field);
MethodHandle getter = lookup.unreflectGetter(field);
setter.invoke("new value");
String value = (String) getter.invoke(); // value == "new value"
4.3 从 MethodHandle 回到 Reflection
java
Method methodRef = MethodHandles.reflectAs(Method.class, methodHandle);
Field fieldRef = MethodHandles.reflectAs(Field.class, getter);
⚠️ 注意:如果 MethodHandle 被做过"变换"(如 filterArguments、foldArguments 等),就 无法再反射回去。
5. 实战类比
- Reflection:像"目录 + 说明书",能帮你快速找到有哪些按钮和功能,但操作起来比较慢。
- Method Handles:像"遥控器 + 宏指令",一旦设定好,就能高效快速地执行操作,但前提是你要知道具体哪个按钮。
6. 总结
- 不要把 Method Handles 当成 Reflection 的替代品,两者定位不同。
- Reflection = 结构扫描 & 元信息获取。
- Method Handles = 高性能调用 & 灵活的函数式适配。
- 最佳实践 :
- 扫描 → 用 Reflection。
- 调用 → 转换为 MethodHandle 执行。
✅ 补充示例:框架里的应用
- Spring 在启动时用 Reflection 扫描 Bean 定义。
- JDK 内部在
LambdaMetafactory里用 Method Handles 生成高性能的 Lambda 表达式实现。