52

我正在研究 JDK 1.7 的新功能,但我无法理解 MethodHandle 的设计用途?我理解(直接)调用静态方法(以及在这种情况下直接使用核心反射 API)。我也理解(直接)调用虚拟方法(非静态,非最终)(以及使用需要通过类的层次结构的核心反射 API obj.getClass().getSuperclass())。非虚方法的调用可以视为前者的特例。

是的,我知道超载存在问题。如果要调用方法,则必须提供确切的签名。您不能以简单的方式检查重载方法。

但是,MethodHandle 是关于什么的?反射 API 允许您“查看”对象内部而无需任何预先假设(如实现接口)。您可以出于某种目的检查对象。但是 MethodHandle 又是怎样设计的呢?为什么以及何时应该使用它?

更新:我现在正在阅读这篇http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html文章。据它说,主要目标是简化运行在 JVM 之上的脚本语言的生活,而不是简化 Java 语言本身的生活。

UPDATE-2:我读完上面的链接,从那里引用一些:

JVM 将成为构建动态语言的最佳 VM,因为它已经是动态语言 VM。InvokeDynamic 通过向一流的 JVM 公民推广动态语言,将证明这一点。

使用反射来调用方法效果很好......除了一些问题。方法对象必须从特定类型中检索,不能以一般方式创建。<...>

...反射调用比直接调用慢很多。多年来,JVM 在快速实现反射调用方面做得非常好。现代 JVM 实际上会在幕后生成一堆代码,以避免旧 JVM 处理的大量开销。但简单的事实是,通过任意层数的反射访问总是比直接调用慢,部分原因是完全通用的“调用”方法必须检查并重新检查接收器类型、参数类型、可见性和其他细节,但是还因为参数必须都是对象(因此原语被对象装箱)并且必须作为数组提供以涵盖所有可能的参数(因此参数被装箱)。

对于执行一些反射调用的库来说,性能差异可能并不重要,特别是如果这些调用主要是在内存中动态设置一个静态结构,它可以对其进行正常调用。但是在动态语言中,每次调用都必须使用这些机制,这会严重影响性能。

http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html

因此,对于 Java 程序员来说,它本质上是无用的。我对吗?从这个角度来看,它只能被认为是Core Reflection API的替代方式。

UPDATE-2020:确实,MethodHandle 可以被认为是核心反射 API 的更强大的替代方案。从 JDK 8 开始,还有一些使用它的 Java 语言特性。

4

4 回答 4

34

你可以用 MethodHandles 做的是 curry 方法,改变参数的类型和改变它们的顺序。

方法句柄可以处理方法和字段。

MethodHandles 做的另一个技巧是使用原始直接(而不是通过包装器)

MethodHandles 可以比使用反射更快,因为在 JVM 中有更直接的支持,例如它们可以内联。它使用新的 invokedynamic 指令。

于 2012-01-11T17:37:54.140 回答
11

java.lang.reflect.Method在内存方面相对较慢且昂贵。方法句柄应该是一种“轻量级”的方式来传递指向 JVM 有机会优化的函数的指针。从 JDK8 开始,方法句柄并没有得到很好的优化,并且 lambda 很可能最初是根据类来实现的(就像内部类一样)。

于 2012-01-11T17:34:36.087 回答
11

将 MethodHandle 视为一种现代、更灵活、更类型安全的反射方式。

它目前处于其生命周期的早期阶段——但随着时间的推移,它有可能被优化为必须比反射更快——以至于它可以变得与常规方法调用一样快。

于 2012-01-11T19:44:14.700 回答
5

自从我问这个问题以来已经快 9 年了。JDK 14 是最后一个大量使用 MethodHandle 的稳定版本...我创建了关于invokedynamic https://alex-ber.medium.com/explaining-invokedynamic-introduction-part-i-1079de618512的迷你系列文章。下面,我引用了他们的相关部分。

MethodHandle 可以被认为是核心反射 API 的更强大的替代方案。MethodHandle 是这样一个对象,它存储有关方法(构造函数、字段或类似的低级操作)的元数据,例如方法的方法签名的名称等。采用它的一种方法是指向的指针的目的地方法(取消引用的方法(构造函数、字段或类似的低级操作))。

Java 代码可以创建直接访问该代码可访问的任何方法、构造函数或字段的方法句柄。这是通过称为 MethodHandles.Lookup 的基于功能的反射 API 完成的。例如,可以从 Lookup.findStatic 获得静态方法句柄。还有来自 Core Reflection API 对象的转换方法,例如 Lookup.unreflect。

了解 Core Reflection API 和 MethodHandle 的两个关键区别很重要。

  • 使用 MethodHandle 访问检查仅在构造时进行一次,使用 Core Reflection API 则在每次调用调用方法时完成(并且每次调用安全管理器,降低性能)。

  • Core Reflection API 调用方法是常规方法。在 MethodHandle 中,所有的 invoke* 变化都是签名多态方法。

基本上,访问检查意味着您是否可以访问方法(构造函数、字段或类似的低级操作)。例如,如果方法(构造函数、字段或类似的低级操作)是私有的,您通常不能调用它(从字段中获取值)。

与 Reflection API 不同,JVM 可以完全透视 MethodHandles,并尝试对其进行优化,从而获得更好的性能。

注意:使用MethodHandle,您还可以生成实现逻辑。有关详细信息,请参阅Dynamical hashCode implementation. Part V https://alex-ber.medium.com/explaining-invokedynamic-dynamical-hashcode-implementation-part-v-16eb318fcd47

于 2020-09-08T15:56:34.610 回答