模块 java.base

类 MethodHandle

java.lang.Object
java.lang.invoke.MethodHandle
所有已实现的接口:
Constable

public abstract sealed class MethodHandle extends Object implements Constable
方法句柄是对底层方法、构造函数、字段或类似低级操作的类型化、直接可执行引用,具有参数或返回值的可选转换。这些转换非常通用,包括 转换insertion删除代换 等模式。

方法句柄内容

方法句柄根据它们的参数和返回类型进行动态和强类型化。它们不以名称或底层方法的定义类来区分。必须使用与方法句柄自己的 类型描述符 匹配的符号类型描述符来调用方法句柄。

每个方法句柄都通过 type 访问器报告其类型描述符。这个类型描述符是一个 MethodType 对象,其结构是一系列类,其中一个是方法的返回类型(如果没有则为 void.class )。

方法句柄的类型控制它接受的调用类型,以及适用于它的转换类型。

方法句柄包含一对称为 invokeExact invoke 的特殊调用程序方法。两种调用程序方法都提供对方法句柄的基础方法、构造函数、字段或其他操作的直接访问,这些操作由参数和返回值的转换修改。两个调用者都接受与方法句柄自身类型完全匹配的调用。普通的、不精确的调用程序还接受一系列其他调用类型。

方法句柄是不可变的并且没有可见状态。当然,它们可以绑定到显示状态的底层方法或数据。对于 Java 内存模型,任何方法句柄的行为就好像它的所有(内部)字段都是最终变量一样。这意味着任何对应用程序可见的方法句柄将始终完全形成。即使方法句柄是通过数据竞争中的共享变量发布的,也是如此。

方法句柄不能由用户子类化。实现可能(或可能不会)创建 MethodHandle 的内部子类,这些子类可能通过 Object.getClass 操作可见。程序员不应该从其特定类中得出有关方法句柄的结论,因为方法句柄类层次结构(如果有的话)可能会不时发生变化,或者在不同供应商的实现之间发生变化。

方法句柄编译

命名为 invokeExactinvoke 的 Java 方法调用表达式可以从 Java 源代码调用方法句柄。从源代码的角度来看,这些方法可以接受任何参数,并且它们的结果可以转换为任何返回类型。形式上,这是通过为调用程序方法提供 Object 返回类型和变量 arity Object 参数来实现的,但它们具有称为 signature polymorphism 的附加特性,它将这种调用自由直接连接到 JVM 执行堆栈。

与虚拟方法一样,对 invokeExactinvoke 的源代码级调用编译为 invokevirtual 指令。更不寻常的是,编译器必须记录实际的参数类型,并且可能不会对参数执行方法调用转换。相反,它必须生成指令,根据它们自己未转换的类型将它们压入堆栈。方法句柄对象本身在参数之前被压入堆栈。然后,编译器生成一条 invokevirtual 指令,该指令使用描述参数和返回类型的符号类型描述符调用方法句柄。

要发出完整的符号类型描述符,编译器还必须确定返回类型。这是基于对方法调用表达式的强制转换(如果有),或者如果调用是表达式则为 Object,如果调用是语句则为 void。转换可能是原始类型(但不是 void )。

作为极端情况,未转换的 null 参数被赋予 java.lang.Void 的符号类型描述符。 Void 类型的歧义是无害的,因为除了 null 引用之外没有 Void 类型的引用。

方法句柄调用

第一次执行 invokevirtual 指令时,通过符号解析指令中的名称并验证方法调用是否静态合法来链接它。这也适用于对 invokeExactinvoke 的调用。在这种情况下,将检查编译器发出的符号类型描述符的语法是否正确,并解析其中包含的名称。因此,调用方法句柄的 invokevirtual 指令将始终链接,只要符号类型描述符在句法上格式正确并且类型存在。

链接后执行invokevirtual时,JVM 首先检查接收方法句柄的类型,以确保它与符号类型描述符匹配。如果类型匹配失败,则意味着调用者正在调用的方法不存在于正在调用的单个方法句柄上。

invokeExact 的情况下,调用的类型描述符(在解析符号类型名称之后)必须与接收方法句柄的方法类型完全匹配。在简单的、不精确的 invoke 的情况下,解析的类型描述符必须是接收方 asType 方法的有效参数。因此,普通 invokeinvokeExact 更宽松。

类型匹配后,直接调用 invokeExact 并立即调用方法句柄的底层方法(或其他行为,视情况而定)。

如果调用者指定的符号类型描述符与方法句柄自己的类型完全匹配,则对普通 invoke 的调用与对 invokeExact 的调用相同。如果类型不匹配,invoke 会尝试调整接收方法句柄的类型,就像调用 asType 一样,以获得完全可调用的方法句柄 M2。这允许在调用者和被调用者之间进行更强大的方法类型协商。

Note: 调整后的方法句柄 M2 不能直接观察到,因此不需要实现来实现它。)

调用检查

在典型的程序中,方法句柄类型匹配通常会成功。但是,如果匹配失败,JVM 将抛出一个 WrongMethodTypeException ,直接(在 invokeExact 的情况下)或间接地,就像对 asType 的失败调用(在 invoke 的情况下)一样。

因此,在静态类型程序中可能显示为链接错误的方法类型不匹配在使用方法句柄的程序中可能显示为动态 WrongMethodTypeException

因为方法类型包含“活动的”Class 对象,所以方法类型匹配会同时考虑类型名称和类加载器。因此,即使在一个类加载器 L1 中创建方法句柄 M 并在另一个类加载器 L2 中使用,方法句柄调用也是类型安全的,因为在 L2 中解析的调用者的符号类型描述符与原始被调用方法的符号类型相匹配描述符,已在 L1 中解决。 L1 中的解析发生在创建 M 并分配其类型时,而 L2 中的解析发生在链接 invokevirtual 指令时。

除了类型描述符检查之外,方法句柄调用其底层方法的能力不受限制。如果方法句柄由有权访问该方法的类在非公共方法上形成,则生成的句柄可以由接收到它的引用的任何调用者在任何地方使用。

与 Core Reflection API 不同,每次调用反射方法时都会检查访问,执行方法句柄访问检查 创建方法句柄时 。在 ldc 的情况下(见下文),访问检查是作为链接常量方法句柄下的常量池条目的一部分执行的。

因此,非公共方法或非公共类中方法的句柄通常应该保密。它们不应传递给不受信任的代码,除非不受信任的代码使用它们是无害的。

方法句柄创建

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

与类和字符串一样,对应于可访问字段、方法和构造函数的方法句柄也可以直接在类文件的常量池中表示为要由ldc字节码加载的常量。一种新型常量池条目 CONSTANT_MethodHandle 直接引用关联的 CONSTANT_MethodrefCONSTANT_InterfaceMethodrefCONSTANT_Fieldref 常量池条目。 (有关方法句柄常量的完整详细信息,请参阅 Java 虚拟机规范的 4.4.85.4.3.5 部分。)

通过查找或从具有变量 arity 修饰符位 (0x0080) 的方法或构造函数的常量加载生成的方法句柄具有相应的变量 arity,就好像它们是在 asVarargsCollector withVarargs 的帮助下定义的一样。

方法引用可以引用静态或非静态方法。在非静态情况下,方法句柄类型包括一个显式的接收者参数,放在任何其他参数之前。在方法句柄的类型中,初始接收者参数根据最初请求该方法的类进行类型化。 (例如,如果通过 ldc 获得非静态方法句柄,则接收者的类型是常量池条目中命名的类。)

方法句柄常量受制于相同的链接时访问检查其相应的字节码指令,如果字节码行为会抛出此类错误,ldc 指令将抛出相应的链接错误。

作为此的必然结果,对受保护成员的访问仅限于访问类或其子类之一的接收者,并且访问类必须反过来是受保护成员定义类的子类(或包兄弟)。如果方法引用引用当前包外的类的受保护非静态方法或字段,则接收者参数将缩小为访问类的类型。

当调用虚方法的方法句柄时,总是在接收者(即第一个参数)中查找该方法。

还可以创建特定虚拟方法实现的非虚拟方法句柄。这些不根据接收器类型执行虚拟查找。这样的方法句柄模拟 invokespecial 指令对同一方法的影响。还可以创建非虚拟方法句柄来模拟 invokevirtualinvokeinterface 指令对私有方法(如适用)的影响。

使用示例

以下是一些使用示例:
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invokeExact("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s, "nanny");
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assertEquals(s, "savvy");
// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invoke("one", "two");
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList("one","two"));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList(1,2,3));
// mt is ()int
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
 
上述对 invokeExact 或普通 invoke 的每个调用都会生成一条 invokevirtual 指令,其符号类型描述符在以下注释中指示。在这些示例中,辅助方法 assertEquals 被假定为一个在其参数上调用 Objects.equals 并断言结果为真的方法。

异常

方法 invokeExactinvoke 被声明为抛出 Throwable ,也就是说对于方法句柄可以抛出的内容没有静态限制。由于 JVM 不区分已检查异常和未检查异常(当然,除了根据它们的类),将已检查异常归因于方法句柄调用对字节码形状没有特别影响。但是在 Java 源代码中,执行方法句柄调用的方法必须显式抛出 Throwable ,或者必须在本地捕获所有可抛出的对象,仅重新抛出上下文中合法的,并包装非法的。

签名多态性

invokeExact 和普通 invoke 的异常编译和链接行为由术语 signature polymorphism 引用。正如 Java 语言规范中所定义的,签名多态方法是一种可以使用多种调用签名和返回类型中的任何一种进行操作的方法。

在源代码中,无论请求的符号类型描述符如何,对签名多态方法的调用都会编译。像往常一样,Java 编译器针对命名方法发出带有给定符号类型描述符的 invokevirtual 指令。不寻常的部分是符号类型描述符是从实际参数和返回类型派生的,而不是从方法声明派生的。

当 JVM 处理包含签名多态调用的字节码时,它将成功链接任何此类调用,而不管其符号类型描述符如何。 (为了保持类型安全,JVM 将使用适当的动态类型检查来保护此类调用,如其他地方所述。)

字节码生成器(包括编译器后端)需要为这些方法发出未转换的符号类型描述符。确定符号链接的工具需要接受此类未转换的描述符,而不报告链接错误。

方法句柄和核心反射 API 之间的互操作

使用 Lookup API 中的工厂方法,任何由核心反射 API 对象表示的类成员都可以转换为行为等效的方法句柄。例如,可以使用 Lookup.unreflect 将反射 Method 转换为方法句柄。生成的方法句柄通常提供对底层类成员的更直接和有效的访问。

作为一种特殊情况,当使用 Core Reflection API 查看此类中的签名多态方法 invokeExact 或普通的 invoke 时,它们显示为普通的非多态方法。它们的反射外观,如 Class.getDeclaredMethod 所见,不受它们在此 API 中的特殊状态的影响。例如,Method.getModifiers 将准确报告任何类似声明的方法所需的修饰符位,在本例中包括 nativevarargs 位。

与任何反射方法一样,这些方法(反射时)可以通过 java.lang.reflect.Method.invoke 调用。但是,此类反射调用不会导致方法句柄调用。这样的调用,如果传递了所需的参数(单个参数,类型为 Object[] ),将忽略该参数并抛出一个 UnsupportedOperationException

由于 invokevirtual 指令可以在任何符号类型描述符下本地调用方法句柄,因此这种反射视图与通过字节码对这些方法的正常表示相冲突。因此,当 Class.getDeclaredMethod 反思时,这两个本机方法可能仅被视为占位符。

为了获得特定类型描述符的调用方法,请使用 MethodHandles.exactInvoker MethodHandles.invoker Lookup.findVirtual API 还能够返回一个方法句柄来调用 invokeExact 或简单的 invoke ,用于任何指定的类型描述符。

方法句柄和 Java 泛型之间的互操作

可以在使用 Java 泛型类型声明的方法、构造函数或字段上获取方法句柄。与 Core Reflection API 一样,方法句柄的类型将从源级类型的擦除中构造。调用方法句柄时,其参数类型或返回值转换类型可能是泛型类型或类型实例。如果发生这种情况,编译器将在为 invokevirtual 指令构造符号类型描述符时用擦除替换这些类型。

方法句柄在 Java 参数化(泛型)类型方面并不表示它们的类函数类型,因为类函数类型和参数化 Java 类型之间存在三个不匹配。

  • 方法类型涵盖了所有可能的参数,从没有参数到最多 最大数量 个允许的参数。泛型不是可变的,因此不能表示这一点。
  • 方法类型可以指定基本类型的参数,而 Java 泛型类型不能重写这些参数。
  • 方法句柄(组合器)上的高阶函数通常在广泛的函数类型中是通用的,包括那些具有多个参数的函数类型。用 Java 类型参数来表示这种通用性是不可能的。

数量限制

JVM 对所有方法和任何类型的构造方法施加了 255 个堆叠参数的绝对限制。在某些情况下,此限制可能会显得更加严格:
  • longdouble 参数计为两个参数槽(出于数量限制的目的)。
  • 非静态方法为调用该方法的对象消耗一个额外的参数。
  • 构造方法为正在构造的对象消耗一个额外的参数。
  • 由于方法句柄的 invoke 方法(或其他签名多态方法)是非虚拟的,因此除了任何非虚拟接收器对象之外,它还为方法句柄本身消耗一个额外的参数。
这些限制意味着无法创建某些方法句柄,这仅仅是因为 JVM 对堆栈参数的限制。例如,如果静态 JVM 方法正好接受 255 个参数,则无法为其创建方法句柄。尝试使用不可能的方法类型创建方法句柄会导致 IllegalArgumentException 。特别是,方法句柄的类型不得具有精确的最大值 255 的元数。
自从:
1.7
参见:
  • 方法总结

    修饰符和类型
    方法
    描述
    asCollector(int collectArgPos, Class<?> arrayType, int arrayLength)
    创建一个 array-collecting 方法句柄,它接受从给定位置开始的给定数量的位置参数,并将它们收集到一个数组参数中。
    asCollector(Class<?> arrayType, int arrayLength)
    创建一个 array-collecting 方法句柄,它接受给定数量的尾随位置参数并将它们收集到一个数组参数中。
    生成一个 fixed arity 方法句柄,它在其他方面等同于当前方法句柄。
    asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength)
    创建一个 array-spreading 方法句柄,它在给定位置接受数组参数,并将其元素作为位置参数展开以代替数组。
    asSpreader(Class<?> arrayType, int arrayLength)
    创建一个 array-spreading 方法句柄,它接受尾随数组参数并将其元素作为位置参数展开。
    asType(MethodType newType)
    生成一个适配器方法句柄,它使当前方法句柄的类型适应新类型。
    asVarargsCollector(Class<?> arrayType)
    制作一个 variable arity 适配器,它能够接受任意数量的尾随位置参数并将它们收集到一个数组参数中。
    将值 x 绑定到方法句柄的第一个参数,而不调用它。
    如果可以构造,则返回此实例的标称描述符,如果不能构造,则返回一个空的Optional
    final Object
    invoke(Object... args)
    调用方法句柄,允许任何调用者类型描述符,并可选择对参数和返回值执行转换。
    final Object
    invokeExact(Object... args)
    调用方法句柄,允许任何调用者类型描述符,但需要精确的类型匹配。
    invokeWithArguments(Object... arguments)
    执行变量 arity 调用,将给定数组中的参数传递给方法句柄,就像通过来自调用站点的不精确 invoke 一样,该调用站点仅提及类型 Object ,其实际参数计数是参数数组的长度。
    invokeWithArguments(List<?> arguments)
    执行变量 arity 调用,将给定列表中的参数传递给方法句柄,就像通过来自调用站点的不精确 invoke 一样,该调用站点仅提及类型 Object ,其实际参数计数是参数列表的长度。
    boolean
    确定此方法句柄是否支持 变数 调用。
    返回方法句柄的字符串表示形式,以字符串 "MethodHandle" 开头,以方法句柄类型的字符串表示形式结束。
    type()
    报告此方法句柄的类型。
    withVarargs(boolean makeVarargs)
    如果布尔标志为真,则将此方法句柄调整为 变数,否则调整为 固定数量

    在类 java.lang.Object 中声明的方法

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • 方法详情

    • type

      public MethodType  type()
      报告此方法句柄的类型。每次通过 invokeExact 调用此方法句柄都必须与此类型完全匹配。
      返回:
      方法句柄类型
    • invokeExact

      public final Object  invokeExact(Object ... args) throws Throwable
      调用方法句柄,允许任何调用者类型描述符,但需要精确的类型匹配。 invokeExact 调用站点的符号类型描述符必须与此方法句柄的 type 完全匹配。参数或返回值不允许转换。

      当通过 Core Reflection API 观察此方法时,它将显示为单个本机方法,获取一个对象数组并返回一个对象。如果通过 java.lang.reflect.Method.invoke 、JNI 或间接通过 Lookup.unreflect 直接调用此本地方法,它将抛出 UnsupportedOperationException

      参数:
      args - 签名多态参数列表,使用可变参数静态表示
      返回:
      签名多态结果,使用 Object 静态表示
      抛出:
      WrongMethodTypeException - 如果目标的类型与调用者的符号类型描述符不同
      Throwable - 底层方法抛出的任何内容都会通过方法句柄调用传播
    • invoke

      public final Object  invoke(Object ... args) throws Throwable
      调用方法句柄,允许任何调用者类型描述符,并可选择对参数和返回值执行转换。

      如果调用站点的符号类型描述符与此方法句柄的 type 完全匹配,则调用将按照 invokeExact 进行。

      否则,调用将继续进行,就像首先通过调用 asType 将此方法句柄调整为所需类型来调整此方法句柄一样,然后调用将继续进行,就好像由 invokeExact 在调整后的方法句柄上进行调整一样。

      无法保证确实进行了 asType 调用。如果 JVM 可以预测调用的结果,它可以直接对调用者的参数进行适配,并根据自己的确切类型调用目标方法句柄。

      invoke 调用站点上解析的类型描述符必须是接收方 asType 方法的有效参数。特别是,如果被调用者不是 变量收集器 ,则调用者必须指定与被调用者类型相同的参数元数。

      当通过 Core Reflection API 观察此方法时,它将显示为单个本机方法,获取一个对象数组并返回一个对象。如果通过 java.lang.reflect.Method.invoke 、JNI 或间接通过 Lookup.unreflect 直接调用此本地方法,它将抛出 UnsupportedOperationException

      参数:
      args - 签名多态参数列表,使用可变参数静态表示
      返回:
      签名多态结果,使用 Object 静态表示
      抛出:
      WrongMethodTypeException - 如果目标的类型不能调整为调用者的符号类型描述符
      ClassCastException - 如果目标的类型可以调整为调用者,但引用转换失败
      Throwable - 底层方法抛出的任何内容都会通过方法句柄调用传播
    • invokeWithArguments

      public Object  invokeWithArguments(Object ... arguments) throws Throwable
      执行变量 arity 调用,将给定数组中的参数传递给方法句柄,就像通过来自调用站点的不精确 invoke 一样,该调用站点仅提及类型 Object ,其实际参数计数是参数数组的长度。

      具体来说,执行过程就像按照以下步骤进行一样,尽管如果 JVM 可以预测它们的效果,则不能保证调用这些方法。

      • 确定参数数组的长度为 N 。对于空引用,N=0
      • 收集数组的 N 元素作为逻辑参数列表,每个参数静态类型化为 Object
      • 确定,作为 M ,此方法句柄的类型的参数计数。
      • 确定 N 参数或 M 参数的一般类型 TN,如果小于 N,则为 TN=MethodType.genericMethodType(Math.min(N, M))
      • 如果 N 大于 M ,则执行以下检查和操作以缩短逻辑参数列表:
        • 检查此方法句柄是否具有具有某种数组类型 A[]尾随参数 的变量 arity。如果不是,则失败并返回 WrongMethodTypeException
        • 将逻辑参数列表中的尾随元素(有 N-M+1 个)收集到类型为 A[] 的单个数组中,使用 asType 转换将每个尾随参数转换为类型 A
        • 如果这些转换中的任何一个被证明是不可能的,如果任何尾随元素不能转换为 A 则失败并返回 ClassCastException 或如果任何尾随元素是 nullA 不是引用类型则返回 NullPointerException
        • 将收集到类型 A[] 的数组中的逻辑参数替换为数组本身,从而将参数列表缩短为长度 M 。最后一个参数保留静态类型 A[]
        • 通过将第 N 个参数类型从 Object 更改为 A[] 来调整类型 TN
      • 将原始目标方法句柄 MH0 强制为所需类型,如 MH1 = MH0.asType(TN)
      • 将参数列表分散到 N 个单独的参数 A0, ... 中。
      • 在解压缩的参数上调用类型调整的方法句柄:MH1.invokeExact(A0, ...)。
      • 将返回值作为Object参考。

      如果目标方法句柄具有变量 arity,并且参数列表比该 arity 长,则从尾随数组参数的位置开始的多余参数将被收集(如果可能,就好像通过 asType 转换)到一个数组中适当的类型,调用将在缩短的参数列表上进行。这样,本来要散布到254个以上slot的jumbo argument lists还是可以统一处理的。

      generic 调用模式不同,它可以“回收”数组参数,将其直接传递给目标方法,此调用模式 always 创建一个新的数组参数,即使传递给 invokeWithArguments 的原始数组作为直接参数可以接受目标方法。即使实际参数的数量 M 是元数 N ,并且最后一个参数动态地是一个合适的类型 A[] 数组,它仍然会被装箱到一个新的单元素数组中,因为调用站点将参数静态类型为 Object ,不是数组类型。这不是此方法的特殊规则,而是 变量调用规则 的常规效果。

      由于 asType 步骤的操作,将根据需要应用以下参数转换:

      • 参考铸造
      • 开箱
      • 扩大原始转换
      • 可变元数转换

      如果调用返回的结果是原语,则将其装箱;如果返回类型为 void,则强制为 null。

      与签名多态方法 invokeExactinvoke 不同,invokeWithArguments 可以通过 Core Reflection API 和 JNI 正常访问。因此,它可以用作本机或反射代码与方法句柄之间的桥梁。

      API 注意:
      此调用大致等同于以下代码:
      // for jumbo argument lists, adapt varargs explicitly:
      int N = (arguments == null? 0: arguments.length);
      int M = this.type.parameterCount();
      int MAX_SAFE = 127; // 127 longs require 254 slots, which is OK
      if (N > MAX_SAFE && N > M && this.isVarargsCollector()) {
       Class<?> arrayType = this.type().lastParameterType();
       Class<?> elemType = arrayType.getComponentType();
       if (elemType != null) {
        Object args2 = Array.newInstance(elemType, M);
        MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType);
        for (int i = 0; i < M; i++) {
         arraySetter.invoke(args2, i, arguments[M-1 + i]);
        }
        arguments = Arrays.copyOf(arguments, M);
        arguments[M-1] = args2;
        return this.asFixedArity().invokeWithArguments(arguments);
       }
      } // done with explicit varargs processing
      
      // Handle fixed arity and non-jumbo variable arity invocation.
      MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
      Object result = invoker.invokeExact(this, arguments);
       
      参数:
      arguments - 传递给目标的参数
      返回:
      目标返回的结果
      抛出:
      ClassCastException - 如果无法通过引用转换转换参数
      WrongMethodTypeException - 如果无法调整目标类型以采用给定数量的 Object 参数
      Throwable - 目标方法调用抛出的任何东西
      参见:
    • invokeWithArguments

      public Object  invokeWithArguments(List <?> arguments) throws Throwable
      执行变量 arity 调用,将给定列表中的参数传递给方法句柄,就像通过来自调用站点的不精确 invoke 一样,该调用站点仅提及类型 Object ,其实际参数计数是参数列表的长度。

      这个方法也等同于下面的代码:

       invokeWithArguments(arguments.toArray())
       

      如果此方法句柄具有可变元数,则可以接受巨型列表。有关详细信息,请参阅 invokeWithArguments(Object[])

      参数:
      arguments - 传递给目标的参数
      返回:
      目标返回的结果
      抛出:
      NullPointerException - 如果 arguments 是空引用
      ClassCastException - 如果无法通过引用转换转换参数
      WrongMethodTypeException - 如果无法调整目标类型以采用给定数量的 Object 参数
      Throwable - 目标方法调用抛出的任何东西
    • asType

      public final MethodHandle  asType(MethodType  newType)
      生成一个适配器方法句柄,它使当前方法句柄的类型适应新类型。生成的方法句柄保证报告一个等于所需新类型的类型。

      如果原始类型和新类型相等,则返回 this

      调用新方法句柄时,将执行以下步骤:

      • 转换传入参数列表以匹配原始方法句柄的参数列表。
      • 在转换后的参数列表上调用原始方法句柄。
      • 将原始方法句柄返回的任何结果转换为新方法句柄的返回类型。

      此方法提供了 invokeExact 与普通的、不精确的 invoke 之间的关键行为差异。当调用者的类型描述符与被调用者的完全匹配时,这两种方法执行相同的步骤,但当类型不同时,plain invoke 也会调用 asType(或某些内部等效项)以匹配调用者和被调用者的类型。

      如果当前方法是可变元数方法句柄,则参数列表转换可能涉及将多个参数转换和收集到一个数组中,如 别处描述 。在所有其他情况下,所有转换都应用 pairwise ,这意味着每个参数或返回值都转换为一个参数或返回值(或没有返回值)。应用的转换是通过查阅新旧方法句柄类型的相应组件类型来定义的。

      T0T1为对应的新旧参数类型,或新旧返回类型。具体来说,对于某些有效索引 i ,让 T0 =newType.parameterType(i)T1 =this.type().parameterType(i) 。否则,以其他方式获取返回值,让 T0 =this.type().returnType()T1 =newType.returnType() 。如果类型相同,则新方法句柄不会更改相应的参数或返回值(如果有)。否则,如果可能,将应用以下转换之一:

      • 如果 T0T1 是引用,则应用转换为 T1。 (类型不需要以任何特定方式相关。这是因为 null 的动态值可以转换为任何引用类型。)
      • 如果 T0T1 是原语,则应用 Java 方法调用转换 (JLS 5.3)(如果存在)。 (具体来说,T0 必须通过扩展原始转换转换为 T1。)
      • 如果 T0 是原语并且 T1 是引用,则应用 Java 强制转换 (JLS 5.5)(如果存在)。 (具体来说,值从 T0 装箱到它的包装类,然后根据需要扩大到 T1 。)
      • 如果 T0 是引用而 T1 是基元,则将在运行时应用拆箱转换,可能随后对基元值进行 Java 方法调用转换 (JLS 5.3)。 (这些是原始扩展转换。)T0 必须是包装类或其中一个的超类型。 (在 T0 是 Object 的情况下,这些是 java.lang.reflect.Method.invoke 允许的转换。)拆箱转换必须有成功的可能性,这意味着如果 T0 本身不是包装类,则必须存在至少一个包装类 TW 它是 T0 的子类型,其未装箱的原始值可以扩展为 T1
      • 如果返回类型 T1 标记为 void,则丢弃任何返回值
      • 如果返回类型 T0 为 void 且 T1 为引用,则引入空值。
      • 如果返回类型 T0 为 void 且 T1 为原语,则引入零值。
      Note: T0T1 都可以认为是静态类型,因为它们都没有具体对应于任何实际参数或返回值的 dynamic type。)

      如果无法进行任何所需的成对转换,则无法进行方法句柄转换。

      在运行时,应用于引用参数或返回值的转换可能需要额外的运行时检查,这可能会失败。取消装箱操作可能会失败,因为原始引用为空,从而导致 NullPointerException 。取消装箱操作或引用转换也可能在引用错误类型的对象时失败,从而导致 ClassCastException 。尽管拆箱操作可能接受多种包装器,但如果没有可用的包装器,则会抛出 ClassCastException

      参数:
      newType - 新方法句柄的预期类型
      返回:
      在执行任何必要的参数转换后委托给 this 的方法句柄,并安排任何必要的返回值转换
      抛出:
      NullPointerException - 如果 newType 是空引用
      WrongMethodTypeException - 如果无法进行转换
      参见:
    • asSpreader

      public MethodHandle  asSpreader(Class <?> arrayType, int arrayLength)
      创建一个 array-spreading 方法句柄,它接受尾随数组参数并将其元素作为位置参数展开。新方法句柄适应,因为它目标当前方法句柄。适配器的类型将与目标的类型相同,除了目标类型的最终 arrayLength 参数被 arrayType 类型的单个数组参数替换。

      如果数组元素类型与原始目标上的任何相应参数类型不同,则原始目标将调整为直接获取数组元素,就像调用 asType 一样。

      调用时,适配器用数组元素替换尾随数组参数,每个元素作为其自己的目标参数。 (保留参数的顺序。)它们通过转换和/或拆箱成对地转换为目标的尾随参数的类型。最后调用目标。目标最终返回的内容由适配器原封不动地返回。

      在调用目标之前,适配器会验证该数组包含的元素是否恰好足以为目标方法句柄提供正确的参数计数。 (当需要零个元素时,数组也可以为空。)

      当适配器被调用时,所提供的array参数的长度被查询,就像通过array.lengtharraylength字节码一样。如果适配器接受零长度尾随数组参数,则提供的 array 参数可以是零长度数组或 null ;否则,适配器将在数组为 null 时抛出 NullPointerException,在数组没有正确数量的元素时抛出 IllegalArgumentException

      以下是数组扩展方法句柄的一些简单示例:

      MethodHandle equals = publicLookup()
       .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
      assert( (boolean) equals.invokeExact("me", (Object)"me"));
      assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
      // spread both arguments from a 2-array:
      MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
      assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
      assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
      // try to spread from anything but a 2-array:
      for (int n = 0; n <= 10; n++) {
       Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]);
       try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
       catch (IllegalArgumentException ex) { } // OK
      }
      // spread both arguments from a String array:
      MethodHandle eq2s = equals.asSpreader(String[].class, 2);
      assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
      assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
      // spread second arguments from a 1-array:
      MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
      assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
      assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
      // spread no arguments from a 0-array or null:
      MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
      assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
      assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
      // asSpreader and asCollector are approximate inverses:
      for (int n = 0; n <= 2; n++) {
        for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
          MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
          assert( (boolean) equals2.invokeWithArguments("me", "me"));
          assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
        }
      }
      MethodHandle caToString = publicLookup()
       .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
      assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
      MethodHandle caString3 = caToString.asCollector(char[].class, 3);
      assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
      MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
      assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
       
      参数:
      arrayType - 通常是 Object[] ,从中提取传播参数的数组参数的类型
      arrayLength - 从传入数组参数传播的参数数量
      返回:
      在调用原始方法句柄之前传播其最终数组参数的新方法句柄
      抛出:
      NullPointerException - 如果 arrayType 是空引用
      IllegalArgumentException - 如果 arrayType 不是数组类型,或者如果目标没有至少 arrayLength 参数类型,或者如果 arrayLength 为负数,或者如果生成的方法句柄的类型为 太多的参数
      WrongMethodTypeException - 如果隐含的 asType 调用失败
      参见:
    • asSpreader

      public MethodHandle  asSpreader(int spreadArgPos, Class <?> arrayType, int arrayLength)
      创建一个 array-spreading 方法句柄,它在给定位置接受数组参数,并将其元素作为位置参数展开以代替数组。新方法句柄适应,因为它目标当前方法句柄。适配器的类型将与目标的类型相同,除了目标类型的 arrayLength 参数,从零开始的位置 spreadArgPos ,被替换为 arrayType 类型的单个数组参数。

      此方法的行为与 asSpreader(Class, int) 非常相似,但接受一个额外的 spreadArgPos 参数来指示应该在参数列表中的哪个位置进行传播。

      API 注意:
      示例:
        MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
        MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
        Object[] ints = new Object[]{3, 9, 7, 7};
        Comparator<Integer> cmp = (a, b) -> a - b;
        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
       
      参数:
      spreadArgPos - 参数列表中应该开始传播的位置(从零开始的索引)。
      arrayType - 通常是 Object[] ,从中提取传播参数的数组参数的类型
      arrayLength - 从传入数组参数传播的参数数量
      返回:
      在调用原始方法句柄之前,在给定位置传播数组参数的新方法句柄
      抛出:
      NullPointerException - 如果 arrayType 是空引用
      IllegalArgumentException - 如果 arrayType 不是数组类型,或者如果 target 没有至少 arrayLength 参数类型,或者如果 arrayLength 为负数,或者如果 spreadArgPos 具有非法值(负数,或者与 arrayLength 一起超过参数数量),或者如果生成的方法句柄的类型为 太多的参数
      WrongMethodTypeException - 如果隐含的 asType 调用失败
      自从:
      9
      参见:
    • withVarargs

      public MethodHandle  withVarargs(boolean makeVarargs)
      如果布尔标志为真,则将此方法句柄调整为 变数,否则调整为 固定数量。如果方法句柄已经是正确的 arity 模式,则返回不变。
      API 注意:

      当且仅当原始句柄是可变元数时,此方法在适配可能是可变元数的方法句柄时有时很有用,以确保生成的适配器也是可变元数。例如,此代码将句柄的第一个参数 mh 更改为 int 而不会影响其变量 arity 属性:mh.asType(mh.type().changeParameterType(0,int.class)) .withVarargs(mh.isVarargsCollector())

      此调用大致等同于以下代码:

      if (makeVarargs == isVarargsCollector())
       return this;
      else if (makeVarargs)
       return asVarargsCollector(type().lastParameterType());
      else
       return asFixedArity();
       
      参数:
      makeVarargs - 如果返回方法句柄应该有可变的行为,则为真
      返回:
      相同类型的方法句柄,具有可能调整的可变元数行为
      抛出:
      IllegalArgumentException - 如果 makeVarargs 为真且此方法句柄没有尾随数组参数
      自从:
      9
      参见:
    • asCollector

      public MethodHandle  asCollector(Class <?> arrayType, int arrayLength)
      创建一个 array-collecting 方法句柄,它接受给定数量的尾随位置参数并将它们收集到一个数组参数中。新方法句柄适应,因为它目标当前方法句柄。适配器的类型将与目标的类型相同,除了单个尾随参数(通常为 arrayType 类型)被 arrayLength 参数替换,其类型为 arrayType 的元素类型。

      如果数组类型与原始目标上的最终参数类型不同,则原始目标会调整为直接采用数组类型,就像调用 asType 一样。

      调用时,适配器将其尾随的 arrayLength 参数替换为类型为 arrayType 的单个新数组,其元素包含(按顺序)替换的参数。最后调用目标。目标最终返回的内容由适配器原封不动地返回。

      (当 arrayLength 为零时,数组也可以是共享常量。)

      Note: arrayType 通常与原始目标的 最后一个参数类型 相同。它是与 asSpreader 对称的显式参数,也允许目标使用简单的 Object 作为其最后一个参数类型。)

      为了创建一个不限于特定数量的收集参数的收集适配器,请改用 asVarargsCollector withVarargs

      以下是数组收集方法句柄的一些示例:

      MethodHandle deepToString = publicLookup()
       .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
      assertEquals("[won]",  (String) deepToString.invokeExact(new Object[]{"won"}));
      MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
      assertEquals(methodType(String.class, Object.class), ts1.type());
      //assertEquals("[won]", (String) ts1.invokeExact(     new Object[]{"won"})); //FAIL
      assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
      // arrayType can be a subtype of Object[]
      MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
      assertEquals(methodType(String.class, String.class, String.class), ts2.type());
      assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
      MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
      assertEquals("[]", (String) ts0.invokeExact());
      // collectors can be nested, Lisp-style
      MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
      assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
      // arrayType can be any primitive array type
      MethodHandle bytesToString = publicLookup()
       .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
       .asCollector(byte[].class, 3);
      assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
      MethodHandle longsToString = publicLookup()
       .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
       .asCollector(long[].class, 1);
      assertEquals("[123]", (String) longsToString.invokeExact((long)123));
       

      Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。

      参数:
      arrayType - 通常是 Object[] ,将收集参数的数组参数的类型
      arrayLength - 要收集到新数组参数中的参数数量
      返回:
      一个新的方法句柄,它在调用原始方法句柄之前将一些尾随参数收集到一个数组中
      抛出:
      NullPointerException - 如果 arrayType 是空引用
      IllegalArgumentException - 如果 arrayType 不是数组类型或 arrayType 不可分配给此方法句柄的尾随参数类型,或 arrayLength 不是合法的数组大小,或者生成的方法句柄的类型将具有 太多的参数
      WrongMethodTypeException - 如果隐含的 asType 调用失败
      参见:
    • asCollector

      public MethodHandle  asCollector(int collectArgPos, Class <?> arrayType, int arrayLength)
      创建一个 array-collecting 方法句柄,它接受从给定位置开始的给定数量的位置参数,并将它们收集到一个数组参数中。新方法句柄适应,因为它目标当前方法句柄。适配器的类型将与目标的类型相同,只是 collectArgPos 指示的位置的参数(通常为 arrayType 类型)被类型为 arrayType 的元素类型的 arrayLength 参数替换。

      此方法的行为与 asCollector(Class, int) 非常相似,但不同之处在于其 collectArgPos 参数指示应在参数列表中的哪个位置收集参数。该指数是从零开始的。

      API 注意:
      示例:
        StringWriter swr = new StringWriter();
        MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
        MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
        swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
        assertEquals("BC", swr.toString());
        swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
        assertEquals("BCPQRS", swr.toString());
        swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
        assertEquals("BCPQRSZ", swr.toString());
       

      Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。

      参数:
      collectArgPos - 参数列表中从零开始收集的位置。
      arrayType - 通常是 Object[] ,将收集参数的数组参数的类型
      arrayLength - 要收集到新数组参数中的参数数量
      返回:
      一个新的方法句柄,它在调用原始方法句柄之前将一些参数收集到一个数组中
      抛出:
      NullPointerException - 如果 arrayType 是空引用
      IllegalArgumentException - 如果 arrayType 不是数组类型或 arrayType 不可分配给此方法句柄的数组参数类型,或 arrayLength 不是合法的数组大小,或 collectArgPos 具有非法值(负数,或大于参数数),或结果方法句柄的类型将是太多的参数
      WrongMethodTypeException - 如果隐含的 asType 调用失败
      自从:
      9
      参见:
    • asVarargsCollector

      public MethodHandle  asVarargsCollector(Class <?> arrayType)
      制作一个 variable arity 适配器,它能够接受任意数量的尾随位置参数并将它们收集到一个数组参数中。

      适配器的类型和行为将与目标的类型和行为相同,除了某些 invokeasType 请求可能导致尾随位置参数被收集到目标的尾随参数中。此外,适配器的 最后一个参数类型 将为 arrayType ,即使目标具有不同的最后一个参数类型。

      如果方法句柄已经是可变元数并且其尾随参数类型与 arrayType 相同,则此转换可能返回 this

      当使用 invokeExact 调用时,适配器调用目标而不更改参数。 (Note: 此行为不同于 固定数量收集器 ,因为它接受不确定长度的整个数组,而不是固定数量的参数。)

      当使用普通的、不精确的 invoke 调用时,如果调用者类型与适配器相同,适配器将调用目标,就像使用 invokeExact 一样。 (这是类型匹配时 invoke 的正常行为。)

      否则,如果调用者和适配器的 arity 相同,并且调用者的尾随参数类型是与适配器的尾随参数类型相同或可分配给适配器的尾随参数类型的引用类型,则参数和返回值成对转换,就像 asType 在固定的 arity 方法句柄上。

      否则,参数不同,或者适配器的尾随参数类型不能从相应的调用者类型分配。在这种情况下,适配器用类型为 arrayType 的新数组替换从原始尾随参数位置开始的所有尾随参数,其元素包含(按顺序)替换的参数。

      调用者类型必须至少提供足够的参数和正确的类型,以满足目标对尾随数组参数之前的位置参数的要求。因此,调用者必须至少提供 N-1 个参数,其中 N 是目标的元数。此外,必须存在从传入参数到目标参数的转换。与普通 invoke 的其他用途一样,如果未满足这些基本要求,则可能会抛出 WrongMethodTypeException

      在所有情况下,目标最终返回的内容都由适配器原封不动地返回。

      在最后一种情况下,就好像目标方法句柄是用 固定数量收集器 临时调整为调用者类型所需的数量一样。 (与 asCollector 一样,如果数组长度为零,则可以使用共享常量代替新数组。如果对 asCollector 的隐含调用将抛出 IllegalArgumentExceptionWrongMethodTypeException ,则对变量 arity 适配器的调用必须抛出 WrongMethodTypeException 。)

      asType 的行为也专用于可变 arity 适配器,以保持不变,即简单、不精确的 invoke 始终等同于 asType 调用以调整目标类型,然后是 invokeExact 。因此,可变元数适配器通过构建固定元数收集器来响应 asType 请求,当且仅当适配器和请求的类型在元数或尾随参数类型上不同时。生成的固定元数收集器通过成对转换将其类型进一步调整(如有必要)为请求的类型,就像通过 asType 的另一个应用程序一样。

      当通过执行 CONSTANT_MethodHandle 常量的 ldc 指令获得方法句柄时,目标方法被标记为可变元数方法(带有修饰符位 0x0080 ),方法句柄将接受多个元数,就好像方法句柄常量是通过调用 asVarargsCollector 创建。

      为了创建一个收集预定数量参数的收集适配器,并且其类型反映了这个预定数量,请改用asCollector

      没有方法句柄转换会产生具有变量 arity 的新方法句柄,除非它们被记录为这样做。因此,除了 asVarargsCollectorwithVarargs 之外, MethodHandleMethodHandles 中的所有方法都将返回一个具有固定元数的方法句柄,除非指定它们返回其原始操作数(例如,方法句柄自身类型的 asType)。

      在已经是变量 arity 的方法句柄上调用 asVarargsCollector 将生成具有相同类型和行为的方法句柄。它可能(也可能不)返回原始变量 arity 方法句柄。

      这是一个列表制作变量 arity 方法句柄的示例:

      MethodHandle deepToString = publicLookup()
       .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
      MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
      assertEquals("[won]",  (String) ts1.invokeExact(  new Object[]{"won"}));
      assertEquals("[won]",  (String) ts1.invoke(     new Object[]{"won"}));
      assertEquals("[won]",  (String) ts1.invoke(           "won" ));
      assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
      // findStatic of Arrays.asList(...) produces a variable arity method handle:
      MethodHandle asList = publicLookup()
       .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
      assertEquals(methodType(List.class, Object[].class), asList.type());
      assert(asList.isVarargsCollector());
      assertEquals("[]", asList.invoke().toString());
      assertEquals("[1]", asList.invoke(1).toString());
      assertEquals("[two, too]", asList.invoke("two", "too").toString());
      String[] argv = { "three", "thee", "tee" };
      assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
      assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
      List ls = (List) asList.invoke((Object)argv);
      assertEquals(1, ls.size());
      assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
       

      Discussion: 这些规则被设计为变量 arity 方法的 Java 规则的动态类型变体。在这两种情况下,可变 arity 方法或方法句柄的调用者可以传递零个或多个位置参数,或者传递任何长度的预先收集的数组。用户应该知道最终参数的特殊作用,以及类型匹配对最终参数的影响,它决定了单个尾随参数是被解释为整个数组还是被解释为数组的单个元素集。请注意,尾随参数的动态类型对此决定没有影响,只是调用站点的符号类型描述符与方法句柄的类型描述符之间的比较。

      参数:
      arrayType - 通常是 Object[] ,将收集参数的数组参数的类型
      返回:
      一个新的方法句柄,它可以在调用原始方法句柄之前将任意数量的尾随参数收集到一个数组中
      抛出:
      NullPointerException - 如果 arrayType 是空引用
      IllegalArgumentException - 如果 arrayType 不是数组类型或 arrayType 不可分配给此方法句柄的尾随参数类型
      参见:
    • isVarargsCollector

      public boolean isVarargsCollector()
      确定此方法句柄是否支持 变数 调用。此类方法句柄来自以下来源:
      • 打电话给asVarargsCollector
      • 查找方法 的调用解析为变量 arity Java 方法或构造函数
      • CONSTANT_MethodHandleldc 指令解析为变量 arity Java 方法或构造函数
      返回:
      如果此方法句柄接受多个普通的、不精确的 invoke 调用,则为真
      参见:
    • asFixedArity

      public MethodHandle  asFixedArity()
      生成一个 fixed arity 方法句柄,它在其他方面等同于当前方法句柄。

      如果当前方法句柄不是变数,则返回当前方法句柄。即使当前方法句柄不是 asVarargsCollector 的有效输入,也是如此。

      否则,生成的固定元数方法句柄与当前方法句柄具有相同的类型和行为,除了 isVarargsCollector 将为假。固定元数方法句柄可能(也可能不是)是 asVarargsCollector 的前一个参数。

      这是一个列表制作变量 arity 方法句柄的示例:

      MethodHandle asListVar = publicLookup()
       .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
       .asVarargsCollector(Object[].class);
      MethodHandle asListFix = asListVar.asFixedArity();
      assertEquals("[1]", asListVar.invoke(1).toString());
      Exception caught = null;
      try { asListFix.invoke((Object)1); }
      catch (Exception ex) { caught = ex; }
      assert(caught instanceof ClassCastException);
      assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
      try { asListFix.invoke("two", "too"); }
      catch (Exception ex) { caught = ex; }
      assert(caught instanceof WrongMethodTypeException);
      Object[] argv = { "three", "thee", "tee" };
      assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
      assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
      assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
      assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
       
      返回:
      一个新的方法句柄,它只接受固定数量的参数
      参见:
    • bindTo

      public MethodHandle  bindTo(Object  x)
      将值 x 绑定到方法句柄的第一个参数,而不调用它。新方法句柄适应,因为它目标,当前方法通过将其绑定到给定参数来处理。绑定句柄的类型将与目标的类型相同,除了将省略单个前导引用参数。

      调用时,绑定句柄将给定值 x 作为目标的新前导参数插入。其他参数也未更改地传递。目标最终返回的内容由绑定句柄原封不动地返回。

      引用 x 必须可转换为目标的第一个参数类型。

      Note: 因为方法句柄是不可变的,所以目标方法句柄保留其原始类型和行为。

      Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。

      参数:
      x - 绑定到目标第一个参数的值
      返回:
      一个新的方法句柄,它在调用原始方法句柄之前将给定值添加到传入参数列表中
      抛出:
      IllegalArgumentException - 如果目标没有作为引用类型的前导参数类型
      ClassCastException - 如果 x 无法转换为目标的前导参数类型
      参见:
    • describeConstable

      public Optional <MethodHandleDesc > describeConstable()
      如果可以构造,则返回此实例的标称描述符,如果不能构造,则返回一个空的Optional
      指定者:
      describeConstable 在接口 Constable
      返回:
      包含生成的标称描述符的 Optional ,或者如果无法构造则为空的 Optional
      自从:
      12
    • toString

      public String  toString()
      返回方法句柄的字符串表示形式,以字符串 "MethodHandle" 开头,以方法句柄类型的字符串表示形式结束。换句话说,此方法返回一个等于以下值的字符串:
      "MethodHandle" + type().toString()
       

      Note:此 API 的未来版本可能会向字符串表示添加更多信息。因此,应用程序不应解析当前语法。)

      重写:
      toString 在类 Object
      返回:
      方法句柄的字符串表示