517. Java 方法句柄 - 方法句柄 vs 反射 API

0 阅读2分钟

517. Java 方法句柄 - 方法句柄 vs 反射 API

1. 背景介绍

  • Reflection API:早在 JDK1.1 就引入,允许在运行时动态获取类、方法、字段等元信息,并进行调用。它最强的地方是 类成员的“枚举式探索”(比如获取所有方法、字段),这点是 Method Handles 做不到的。
  • Method Handles:JDK7 引入,最初是为了给编译器和语言运行时开发者提供更高效的工具。它不是用来替代 Reflection,而是作为一种 高性能的“方法指针”机制。Method Handles 在调用性能上接近直接调用,但也增加了更灵活的“函数式变换能力”。

2. 访问检查与性能差异

特性Reflection APIMethod Handles
访问检查每次调用都会做一次权限检查(例如 setAccessible(true) 之后仍要验证调用上下文)只在 创建 MethodHandle 时检查一次,后续调用无需再次检查
性能慢(额外安全检查)快(接近普通方法调用)
风险相对安全,控制粒度细一旦 MethodHandle 被错误地传出去,可能导致 非公开成员暴露,需要开发者谨慎

👉 培训提示:可以用“机场安检”做类比:

  • Reflection 每次过闸机都要安检。
  • MethodHandle 只在第一次进入机场时安检,之后在机场里可以自由走动。

3. 功能对比

  • Reflection API
    • 可以枚举和分析类结构(字段、方法、构造器、注解等)。
    • 适合用于 框架扫描、元数据处理,比如 Spring 在启动时扫描 Bean 定义。
  • Method Handles
    • 提供 高性能调用能力。
    • 支持 参数重排、预绑定参数、返回值过滤 等函数式编程风格的操作。
    • 不支持列出类的所有成员。

4. 两者的互操作(Conversion)

在实际开发中,Reflection 和 Method Handles 并不是对立的,而是可以互相转换:

4.1 从 Reflection 到 MethodHandle

// 通过反射获取 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

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

Method methodRef = MethodHandles.reflectAs(Method.class, methodHandle);
Field fieldRef = MethodHandles.reflectAs(Field.class, getter);

⚠️ 注意:如果 MethodHandle 被做过“变换”(如 filterArgumentsfoldArguments 等),就 无法再反射回去


5. 实战类比

  • Reflection:像“目录 + 说明书”,能帮你快速找到有哪些按钮和功能,但操作起来比较慢。
  • Method Handles:像“遥控器 + 宏指令”,一旦设定好,就能高效快速地执行操作,但前提是你要知道具体哪个按钮。

6. 总结

  • 不要把 Method Handles 当成 Reflection 的替代品,两者定位不同。
  • Reflection = 结构扫描 & 元信息获取。
  • Method Handles = 高性能调用 & 灵活的函数式适配。
  • 最佳实践
    • 扫描 → 用 Reflection。
    • 调用 → 转换为 MethodHandle 执行。

补充示例:框架里的应用

  • Spring 在启动时用 Reflection 扫描 Bean 定义。
  • JDK 内部在 LambdaMetafactory 里用 Method Handles 生成高性能的 Lambda 表达式实现。