Module java.base
Package java.lang.invoke

Class MethodHandles

java.lang.Object
java.lang.invoke.MethodHandles

public class MethodHandles extends Object
这个类仅包含操作或返回方法句柄的静态方法。它们分为几个类别:
  • Lookup 方法,用于为方法和字段创建方法句柄。
  • Combinator 方法,将现有的方法句柄组合或转换为新的方法句柄。
  • 其他工厂方法,用于创建模拟其他常见 JVM 操作或控制流模式的方法句柄。
如果创建的方法句柄的类型会有太多参数,查找、组合或工厂方法将失败并抛出 IllegalArgumentException
自 JDK 版本:
1.7
  • Method Details

    • lookup

      public static MethodHandles.Lookup lookup()
      返回一个具有完整功能的 lookup 对象,以模拟调用者的所有支持的字节码行为。这些功能包括对调用者的 完全特权访问。查找对象上的工厂方法可以为调用者具有通过字节码访问的任何成员(包括受保护和私有字段和方法)创建 直接方法句柄。此查找对象由原始查找类创建,并设置了 ORIGINAL 位。此查找对象是一个 能力,可以委托给受信任的代理。不要将其存储在未经信任的代码可以访问的位置。

      此方法是调用者敏感的,这意味着它可能对不同的调用者返回不同的值。在没有调用者帧的情况下调用 MethodHandles.lookup 的情况下(例如,直接从 JNI 附加线程调用时),将抛出 IllegalCallerException。要在这种情况下获取 lookup 对象,请使用一个将隐式标识为调用者的辅助类,或使用 publicLookup() 来获取低特权查找。

      返回:
      为调用此方法的调用者返回一个查找对象,具有原始完全特权访问
      抛出:
      IllegalCallerException - 如果堆栈上没有调用者帧。
    • publicLookup

      public static MethodHandles.Lookup publicLookup()
      返回一个受最低信任的查找对象。该查找具有UNCONDITIONAL模式。它只能用于创建对无条件导出的包中的公共类的公共成员的方法句柄。

      作为纯粹的约定,此查找对象的查找类将是Object

      API 注意:
      使用Object是传统的,由于查找模式受限,因此不提供对Object、其包或其模块的特殊访问。此公共查找对象或其他具有UNCONDITIONAL模式的查找对象假定可读性。因此,查找类不用于确定查找上下文。

      讨论: 可以使用形式为publicLookup().in(C.class)的表达式将查找类更改为任何其他类C。公共查找对象始终受到安全管理器检查。此外,它无法访问调用者敏感方法

      返回:
      一个受最低信任的查找对象
    • privateLookupIn

      public static MethodHandles.Lookup privateLookupIn(Class<?> targetClass, MethodHandles.Lookup caller) throws IllegalAccessException
      返回一个查找对象,用于模拟所有支持的字节码行为,包括私有访问。返回的查找对象可以访问模块和包中的类以及这些类的成员,而不受Java访问控制的正常规则的限制,而是符合模块化的更宽松的深度反射规则。

      指定为Lookup对象的调用者在模块M1中允许对模块M2和目标类的包进行深度反射,当且仅当以下所有条件都为true时:

      • 如果存在安全管理器,则将调用其checkPermission方法来检查ReflectPermission("suppressAccessChecks"),并且必须正常返回。
      • 调用者查找对象必须具有完全特权访问。具体来说:
        • 调用者查找对象必须具有MODULE查找模式。(这是因为否则将无法确保原始查找创建者是任何特定模块的成员,因此任何后续的可读性和限定导出检查都将变得无效。)
        • 调用者查找对象必须具有PRIVATE访问。(这是因为打算仅使用MODULE共享模块内部访问的应用程序将无意中还会共享其自己模块的深度反射。)
      • 目标类必须是一个合适的类,而不是原始类或数组类。(因此,M2是明确定义的。)
      • 如果调用模块M1与目标模块M2不同,则必须同时满足以下两个条件:
        • M1 reads M2
        • M2 opens包含目标类的包至少对M1开放。

      如果违反上述任何检查,此方法将引发异常。

      否则,如果M1M2是同一模块,则此方法返回具有null前一个查找类的完全特权访问Lookup对象。

      否则,M1M2是两个不同的模块。此方法返回在targetClass上的Lookup,将调用者的查找类记录为新的前一个查找类,具有PRIVATE访问但没有MODULE访问。

      生成的Lookup对象没有ORIGINAL访问。

      API 注意:
      此方法返回的Lookup对象允许定义targetClass的运行时包中的类。在向另一个模块打开包时应格外小心,因为这样定义的类具有与targetClass模块中的其他成员相同的完全特权访问。
      参数:
      targetClass - 目标类
      caller - 调用者查找对象
      返回:
      一个用于目标类的查找对象,具有私有访问
      抛出:
      IllegalArgumentException - 如果targetClass是原始类型或void或数组类
      NullPointerException - 如果targetClasscallernull
      SecurityException - 如果安全管理器拒绝
      IllegalAccessException - 如果违反上述任何其他访问检查
      自:
      9
      参见:
    • classData

      public static <T> T classData(MethodHandles.Lookup caller, String name, Class<T> type) throws IllegalAccessException
      Returns the class data associated with the lookup class of the given caller lookup object, or null.

      A hidden class with class data can be created by calling Lookup::defineHiddenClassWithClassData. This method will cause the static class initializer of the lookup class of the given caller lookup object be executed if it has not been initialized.

      A hidden class created by Lookup::defineHiddenClass and non-hidden classes have no class data. null is returned if this method is called on the lookup object on these classes.

      The lookup modes for this lookup must have original access in order to retrieve the class data.

      API Note:
      This method can be called as a bootstrap method for a dynamically computed constant. A framework can create a hidden class with class data, for example that can be Class or MethodHandle object. The class data is accessible only to the lookup object created by the original caller but inaccessible to other members in the same nest. If a framework passes security sensitive objects to a hidden class via class data, it is recommended to load the value of class data as a dynamically computed constant instead of storing the class data in private static field(s) which are accessible to other nestmates.
      Type Parameters:
      T - the type to cast the class data object to
      Parameters:
      caller - the lookup context describing the class performing the operation (normally stacked by the JVM)
      name - must be ConstantDescs.DEFAULT_NAME ("_")
      type - the type of the class data
      Returns:
      the value of the class data if present in the lookup class; otherwise null
      Throws:
      IllegalArgumentException - if name is not "_"
      IllegalAccessException - if the lookup context does not have original access
      ClassCastException - if the class data cannot be converted to the given type
      NullPointerException - if caller or type argument is null
      See Java Virtual Machine Specification:
      5.5 Initialization
      Since:
      16
      See Also:
    • classDataAt

      public static <T> T classDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index) throws IllegalAccessException
      返回指定索引处的元素在类数据中,如果与给定caller查找对象的查找类相关联的类数据是List。如果在此查找类中不存在类数据,则此方法返回null

      可以通过调用Lookup::defineHiddenClassWithClassData创建具有类数据的隐藏类。如果尚未初始化给定caller查找对象的查找类的静态类初始化程序,则此方法将导致其执行。

      通过Lookup::defineHiddenClass创建的隐藏类和非隐藏类没有类数据。如果在这些类上调用此方法,则返回null

      此查找的查找模式必须具有原始访问权限,以检索类数据。

      API 注意:
      此方法可以作为动态计算的常量的引导方法调用。框架可以创建具有类数据的隐藏类,例如可以是List.of(o1, o2, o3....)包含多个对象,并使用此方法在特定索引处加载一个元素。类数据仅对由原始调用者创建的查找对象可访问,但对同一巢层中的其他成员不可访问。如果框架通过类数据将安全敏感对象传递给隐藏类,建议将类数据的值作为动态计算的常量加载,而不是将类数据存储在对其他巢层成员可访问的私有静态字段中。
      类型参数:
      T - 要将结果对象转换为的类型
      参数:
      caller - 描述执行操作的类的查找上下文(通常由JVM堆栈)
      name - 必须是ConstantDescs.DEFAULT_NAME"_"
      type - 类数据中给定索引处的元素的类型
      index - 类数据中元素的索引
      返回:
      如果类数据存在,则返回类数据中给定索引处的元素;否则返回null
      抛出:
      IllegalArgumentException - 如果名称不是"_"
      IllegalAccessException - 如果查找上下文没有原始访问权限
      ClassCastException - 如果类数据无法转换为List或指定索引处的元素无法转换为给定类型
      IndexOutOfBoundsException - 如果索引超出范围
      NullPointerException - 如果callertype参数为null;或者如果解箱操作失败,因为给定索引处的元素为null
      自:
      16
      另请参见:
    • reflectAs

      public static <T extends Member> T reflectAs(Class<T> expected, MethodHandle target)
      执行对direct method handle的未检查“破解”。结果就好像用户已经获得了足够能够破解目标方法句柄的查找对象,调用了目标上的Lookup.revealDirect以获取其符号引用,然后调用MethodHandleInfo.reflectAs来将符号引用解析为成员。

      如果存在安全管理器,则将使用ReflectPermission("suppressAccessChecks")权限调用其checkPermission方法。

      类型参数:
      T - 结果的期望类型,可以是Member或其子类型
      参数:
      expected - 表示所需结果类型T的类对象
      target - 要破解为符号引用组件的直接方法句柄
      返回:
      方法、构造函数或字段对象的引用
      抛出:
      SecurityException - 如果调用者无权调用setAccessible
      NullPointerException - 如果任一参数为null
      IllegalArgumentException - 如果目标不是直接方法句柄
      ClassCastException - 如果成员不是期望的类型
      自:
      1.8
    • arrayConstructor

      public static MethodHandle arrayConstructor(Class<?> arrayClass) throws IllegalArgumentException
      生成一个构造所需类型数组的方法句柄,就好像通过anewarray字节码一样。方法句柄的返回类型将是数组类型。其唯一参数的类型将是int,指定数组的大小。

      如果返回的方法句柄使用负数组大小调用,则将抛出NegativeArraySizeException

      参数:
      arrayClass - 一个数组类型
      返回:
      一个可以创建给定类型数组的方法句柄
      抛出:
      NullPointerException - 如果参数为null
      IllegalArgumentException - 如果arrayClass不是数组类型
      参见Java虚拟机规范:
      6.5 anewarray指令
      自:
      9
      另请参见:
    • arrayLength

      public static MethodHandle arrayLength(Class<?> arrayClass) throws IllegalArgumentException
      生成一个返回数组长度的方法句柄,就好像通过arraylength字节码一样。方法句柄的类型将具有int作为返回类型,其唯一参数将是数组类型。

      如果返回的方法句柄使用null数组引用调用,则将抛出NullPointerException

      参数:
      arrayClass - 一个数组类型
      返回:
      一个可以检索给定数组类型数组长度的方法句柄
      抛出:
      NullPointerException - 如果参数为null
      IllegalArgumentException - 如果arrayClass不是数组类型
      参见Java虚拟机规范:
      6.5 arraylength指令
      自:
      9
    • arrayElementGetter

      public static MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException
      生成一个方法句柄,允许对数组元素进行读取访问,就好像通过aaload字节码一样。方法句柄的类型将具有数组的元素类型作为返回类型。其第一个参数将是数组类型,第二个参数将是int

      当调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用为null,将抛出NullPointerException;如果索引为负数或大于等于数组长度,则将抛出ArrayIndexOutOfBoundsException

      参数:
      arrayClass - 一个数组类型
      返回:
      一个可以从给定数组类型加载值的方法句柄
      抛出:
      NullPointerException - 如果参数为null
      IllegalArgumentException - 如果arrayClass不是数组类型
      参见Java虚拟机规范:
      6.5 aaload指令
    • arrayElementSetter

      public static MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException
      生成一个方法句柄,允许对数组元素进行写入访问,就好像通过astore字节码一样。方法句柄的类型将具有void返回类型。其最后一个参数将是数组的元素类型。第一个和第二个参数将是数组类型和int。

      当调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用为null,将抛出NullPointerException;如果索引为负数或大于等于数组长度,则将抛出ArrayIndexOutOfBoundsException

      参数:
      arrayClass - 数组的类
      返回:
      一个方法句柄,可以将值存储到数组类型中
      抛出:
      NullPointerException - 如果参数为null
      IllegalArgumentException - 如果arrayClass不是数组类型
      参见 Java虚拟机规范:
      6.5 aastore 指令
    • arrayElementVarHandle

      public static VarHandle arrayElementVarHandle(Class<?> arrayClass) throws IllegalArgumentException
      生成一个VarHandle,用于访问类型为 arrayClass 的数组的元素。VarHandle的变量类型是 arrayClass 的组件类型,坐标类型列表为 (arrayClass, int),其中 int 坐标类型对应于作为数组索引的参数。

      返回的VarHandle的某些访问模式在以下情况下不受支持:

      • 如果组件类型不是 byte, short, char, int, long, float, 或 double,则不支持数值原子更新访问模式。
      • 如果组件类型不是 boolean, byte, short, char, int, 或 long,则不支持位原子更新访问模式。

      如果组件类型是 floatdouble,则数值和原子更新访问模式将使用它们的位表示进行比较(参见 Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double))。

      当调用返回的 VarHandle 时,将检查数组引用和数组索引。如果数组引用为 null,将抛出 NullPointerException,如果索引为负数或大于等于数组长度,则将抛出 ArrayIndexOutOfBoundsException

      API 注意:
      float 值或 double 值的位比较,如数值和原子更新访问模式所执行的,与原始的 == 运算符以及 Float.equals(java.lang.Object)Double.equals(java.lang.Object) 方法有所不同,特别是在比较 NaN 值或比较 -0.0+0.0。在使用这些值进行比较和设置或交换操作时应谨慎,因为操作可能会意外失败。在Java中有许多可能被视为 NaN 的 NaN 值,尽管Java提供的任何IEEE 754浮点运算都无法区分它们。如果期望值或参考值是 NaN 值,并且它被转换(可能以特定于平台的方式)为另一个 NaN 值,从而具有不同的位表示(参见 Float.intBitsToFloat(int)Double.longBitsToDouble(long) 了解更多详情)。值 -0.0+0.0 具有不同的位表示,但在使用原始的 == 运算符时被视为相等。例如,如果数值算法计算期望值为 -0.0,并且先前计算的参考值为 +0.0,则操作可能会失败。
      参数:
      arrayClass - 数组的类,类型为 T[]
      返回:
      一个VarHandle,用于访问数组的元素
      抛出:
      NullPointerException - 如果 arrayClass 为 null
      IllegalArgumentException - 如果 arrayClass 不是数组类型
      自 JDK 版本:
      9
    • byteArrayViewVarHandle

      public static VarHandle byteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
      生成一个VarHandle,用于访问将 byte[] 数组视为不同原始数组类型(如 int[]long[])的元素。VarHandle的变量类型是 viewArrayClass 的组件类型,坐标类型列表为 (byte[], int),其中 int 坐标类型对应于作为 byte[] 数组索引的参数。返回的VarHandle访问 byte[] 数组中的索引处的字节,根据给定的字节序将字节组合到或从 viewArrayClass 的组件类型的值中。

      支持的组件类型(变量类型)为 short, char, int, long, floatdouble

      访问给定索引处的字节将导致 ArrayIndexOutOfBoundsException,如果索引小于 0 或大于 byte[] 数组长度减去 T 的大小(以字节为单位)。

      对于 T,在与数组关联的底层内存地址 A 相对于对齐或未对齐的情况下,对索引处的字节进行访问。如果访问未对齐,则除了 getset 访问模式之外,其他访问模式将导致 IllegalStateException。在这种情况下,只能保证相对于 AT 大小(以字节为单位)的最大二的幂的最大公约数。如果访问对齐,则支持以下访问模式,并保证支持原子访问:

      • 对于所有 T,读写访问模式,但在32位平台上,对于 longdoublegetset 访问模式除外。
      • int, long, floatdouble 的原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他类型的特定访问模式。)
      • intlong 的数值原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他数值类型的特定访问模式。)
      • intlong 的位原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他数值类型的特定访问模式。)

      未对齐访问,因此原子性保证,可以在不操作特定数组的情况下确定 byte[] 数组。给定一个 indexT 及其对应的装箱类型 T_BOX,可以如下确定未对齐:

      
       int sizeOfT = T_BOX.BYTES;  // T 的字节大小
       int misalignedAtZeroIndex = ByteBuffer.wrap(new byte[0]).
           alignmentOffset(0, sizeOfT);
       int misalignedAtIndex = (misalignedAtZeroIndex + index) % sizeOfT;
       boolean isMisaligned = misalignedAtIndex != 0;
       

      如果变量类型为 floatdouble,则原子更新访问模式将使用它们的位表示进行比较(参见 Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double))。

      参数:
      viewArrayClass - 视图数组类,具有类型为 T 的组件类型
      byteOrder - 视图数组元素的字节顺序,存储在底层 byte 数组中
      返回:
      一个VarHandle,用于访问将 byte[] 数组的元素视为与视图数组类的组件类型对应的元素
      抛出:
      NullPointerException - 如果 viewArrayClass 或 byteOrder 为 null
      IllegalArgumentException - 如果 viewArrayClass 不是数组类型
      UnsupportedOperationException - 如果 viewArrayClass 的组件类型不受支持作为变量类型
      自 JDK 版本:
      9
    • byteBufferViewVarHandle

      public static VarHandle byteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
      生成一个VarHandle,用于访问将ByteBuffer的元素视为不同原始组件类型的数组的元素,例如int[]long[]。VarHandle的变量类型是viewArrayClass的组件类型,坐标类型列表为(ByteBuffer, int),其中int坐标类型对应于作为byte[]数组索引的参数。返回的VarHandle访问ByteBuffer中的索引处的字节,根据给定的字节顺序将字节组合到或从viewArrayClass的组件类型的值中。

      支持的组件类型(变量类型)包括shortcharintlongfloatdouble

      如果ByteBuffer是只读的,除了读取访问模式之外的任何访问都会导致ReadOnlyBufferException

      访问给定索引处的字节将导致IndexOutOfBoundsException,如果索引小于0或大于ByteBuffer限制减去T的大小(以字节为单位)。

      对于T,访问给定索引处的字节可能是对齐的或未对齐的,相对于与ByteBuffer和索引关联的底层内存地址A。如果访问未对齐,则除了getset访问模式之外的任何访问都会导致IllegalStateException。在这种情况下,原子访问仅针对A的最大二的幂与T的大小(以字节为单位)的最大公约数进行保证。如果访问是对齐的,则支持以下访问模式,并保证支持原子访问:

      • 对于所有T,读写访问模式,但在32位平台上除了longdoublegetset访问模式。
      • intlongfloatdouble的原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他类型。)
      • intlong的数值原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他数值类型。)
      • intlong的位原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他数值类型。)

      对于ByteBufferbb(直接或其他方式)、indexT及其对应的装箱类型T_BOX,可以确定未对齐访问和因此原子性保证如下:

      
       int sizeOfT = T_BOX.BYTES;  // T的字节大小
       ByteBuffer bb = ...
       int misalignedAtIndex = bb.alignmentOffset(index, sizeOfT);
       boolean isMisaligned = misalignedAtIndex != 0;
       

      如果变量类型是floatdouble,则原子更新访问模式将使用它们的位表示进行值比较(参见Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double))。

      参数:
      viewArrayClass - 视图数组类,其组件类型为T
      byteOrder - 视图数组元素的字节顺序,存储在底层ByteBuffer中(注意,这会覆盖ByteBuffer的字节顺序)
      返回:
      生成一个VarHandle,用于访问将ByteBuffer的元素视为与视图数组类的组件类型对应的元素
      抛出:
      NullPointerException - 如果viewArrayClass或byteOrder为null
      IllegalArgumentException - 如果viewArrayClass不是数组类型
      UnsupportedOperationException - 如果viewArrayClass的组件类型不受支持作为变量类型
      自:
      9
    • spreadInvoker

      public static MethodHandle spreadInvoker(MethodType type, int leadingArgCount)
      生成一个方法句柄,该句柄将调用给定type的任何方法句柄,其中给定数量的尾随参数将被替换为单个尾随Object[]数组。生成的调用程序将是一个具有以下参数的方法句柄:
      • 一个单一的MethodHandle目标
      • 零个或多个前导值(由leadingArgCount计数)
      • 包含尾随参数的Object[]数组

      调用程序将像调用invoke一样调用其目标,使用指定的type。也就是说,如果目标恰好是给定的type,它将表现得像invokeExact;否则,它将表现得像asType用于将目标转换为所需的type

      返回的调用程序的类型不会是给定的type,而是除了前leadingArgCount个参数外,所有参数都将替换为单个Object[]类型的数组,这将是最终参数。

      在调用其目标之前,调用程序将展开最终数组,根据需要应用引用转换,并取消装箱和扩展原始参数。如果在调用调用程序时提供的数组参数的元素数量不正确,则调用程序将抛出一个IllegalArgumentException,而不是调用目标。

      此方法等效于以下代码(尽管可能更有效):

      MethodHandle invoker = MethodHandles.invoker(type);
      int spreadArgCount = type.parameterCount() - leadingArgCount;
      invoker = invoker.asSpreader(Object[].class, spreadArgCount);
      return invoker;
      
      此方法不会抛出反射或安全异常。
      参数:
      type - 所需的目标类型
      leadingArgCount - 要传递给目标的固定参数数量
      返回:
      适用于调用给定类型的任何方法句柄的方法句柄
      抛出:
      NullPointerException - 如果type为null
      IllegalArgumentException - 如果leadingArgCount不在从0到type.parameterCount()(包括)的范围内,或者如果生成的方法句柄的类型将具有太多参数
    • exactInvoker

      public static MethodHandle exactInvoker(MethodType type)
      生成一个特殊的调用程序方法句柄,可用于调用给定类型的任何方法句柄,就像通过invokeExact一样。生成的调用程序将具有与所需类型完全相同的类型,只是它将接受一个额外的MethodHandle类型的前导参数。

      此方法等效于以下代码(尽管可能更有效):publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)

      讨论:调用程序方法句柄在处理未知类型的可变方法句柄时可能很有用。例如,要模拟对变量方法句柄M进行invokeExact调用,提取其类型T,查找T的调用程序方法X,并调用调用程序方法,如X.invoke(T, A...)。 (调用X.invokeExact不起作用,因为类型T是未知的。)如果需要传播、收集或其他参数转换,则可以一次应用于调用程序X并在许多M方法句柄值上重用,只要它们与X的类型兼容。

      (注意:调用程序方法不可通过核心反射API获得。尝试在声明的invokeExactinvoke方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException。)

      此方法不会抛出反射或安全异常。

      参数:
      type - 所需的目标类型
      返回:
      适用于调用给定类型的任何方法句柄的方法句柄
      抛出:
      IllegalArgumentException - 如果生成的方法句柄的类型将具有太多参数
    • invoker

      public static MethodHandle invoker(MethodType type)
      生成一个特殊的调用器方法句柄,可用于调用与给定类型兼容的任何方法句柄,就像通过invoke一样。生成的调用器将具有与所需类型完全相同的类型,只是它将接受一个额外的类型为MethodHandle的前导参数。

      在调用其目标之前,如果目标与期望的类型不同,调用器将根据需要应用引用转换,并对原始值进行装箱、拆箱或扩展,就像通过asType一样。类似地,返回值将根据需要进行转换。如果目标是一个可变参数方法句柄,则将进行所需的参数数量转换,再次如同通过asType一样。

      此方法等效于以下代码(尽管可能更有效):publicLookup().findVirtual(MethodHandle.class, "invoke", type)

      讨论:通用方法类型是仅提及Object参数和返回值的方法类型。对于这种类型的调用器能够调用与通用类型相同参数数量的任何方法句柄。

      (注意:调用器方法不可通过核心反射API获得。尝试在声明的invokeExactinvoke方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException。)

      此方法不会抛出反射或安全异常。

      参数:
      type - 所需的目标类型
      返回:
      一个适用于调用任何可转换为给定类型的方法句柄的方法句柄
      抛出:
      IllegalArgumentException - 如果生成的方法句柄的类型将具有太多参数
    • varHandleExactInvoker

      public static MethodHandle varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type)
      生成一个特殊的调用器方法句柄,可用于在与给定类型兼容的任何VarHandle上调用签名多态访问模式方法。生成的调用器将具有与所需给定类型完全相同的类型,只是它将接受一个额外的类型为VarHandle的前导参数。
      参数:
      accessMode - VarHandle访问模式
      type - 所需的目标类型
      返回:
      一个适用于调用任何access mode类型与给定类型兼容的VarHandle的访问模式方法的方法句柄
      自JDK版本:
      9
    • varHandleInvoker

      public static MethodHandle varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type)
      生成一个特殊的调用器方法句柄,可用于在与给定类型兼容的任何VarHandle上调用签名多态访问模式方法。生成的调用器将具有与所需给定类型完全相同的类型,只是它将接受一个额外的类型为VarHandle的前导参数。

      在调用其目标之前,如果访问模式类型与所需给定类型不同,调用器将根据需要应用引用转换,并对原始值进行装箱、拆箱或扩展,就像通过asType一样。类似地,返回值将根据需要进行转换。

      此方法等效于以下代码(尽管可能更有效):publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)

      参数:
      accessMode - VarHandle访问模式
      type - 所需的目标类型
      返回:
      一个适用于调用任何access mode类型可转换为给定类型的VarHandle的访问模式方法的方法句柄
      自JDK版本:
      9
    • explicitCastArguments

      public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType)
      Produces a method handle which adapts the type of the given method handle to a new type by pairwise argument and return type conversion. The original type and new type must have the same number of arguments. The resulting method handle is guaranteed to report a type which is equal to the desired new type.

      If the original type and new type are equal, returns target.

      The same conversions are allowed as for MethodHandle.asType, and some additional conversions are also applied if those conversions fail. Given types T0, T1, one of the following conversions is applied if possible, before or instead of any conversions done by asType:

      • If T0 and T1 are references, and T1 is an interface type, then the value of type T0 is passed as a T1 without a cast. (This treatment of interfaces follows the usage of the bytecode verifier.)
      • If T0 is boolean and T1 is another primitive, the boolean is converted to a byte value, 1 for true, 0 for false. (This treatment follows the usage of the bytecode verifier.)
      • If T1 is boolean and T0 is another primitive, T0 is converted to byte via Java casting conversion (JLS 5.5), and the low order bit of the result is tested, as if by (x & 1) != 0.
      • If T0 and T1 are primitives other than boolean, then a Java casting conversion (JLS 5.5) is applied. (Specifically, T0 will convert to T1 by widening and/or narrowing.)
      • If T0 is a reference and T1 a primitive, an unboxing conversion will be applied at runtime, possibly followed by a Java casting conversion (JLS 5.5) on the primitive value, possibly followed by a conversion from byte to boolean by testing the low-order bit.
      • If T0 is a reference and T1 a primitive, and if the reference is null at runtime, a zero value is introduced.
      Parameters:
      target - the method handle to invoke after arguments are retyped
      newType - the expected type of the new method handle
      Returns:
      a method handle which delegates to the target after performing any necessary argument conversions, and arranges for any necessary return value conversions
      Throws:
      NullPointerException - if either argument is null
      WrongMethodTypeException - if the conversion cannot be made
      See Also:
    • permuteArguments

      public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder)
      Produces a method handle which adapts the calling sequence of the given method handle to a new type, by reordering the arguments. The resulting method handle is guaranteed to report a type which is equal to the desired new type.

      The given array controls the reordering. Call #I the number of incoming parameters (the value newType.parameterCount(), and call #O the number of outgoing parameters (the value target.type().parameterCount()). Then the length of the reordering array must be #O, and each element must be a non-negative number less than #I. For every N less than #O, the N-th outgoing argument will be taken from the I-th incoming argument, where I is reorder[N].

      No argument or return value conversions are applied. The type of each incoming argument, as determined by newType, must be identical to the type of the corresponding outgoing parameter or parameters in the target method handle. The return type of newType must be identical to the return type of the original target.

      The reordering array need not specify an actual permutation. An incoming argument will be duplicated if its index appears more than once in the array, and an incoming argument will be dropped if its index does not appear in the array. As in the case of dropArguments, incoming arguments which are not mentioned in the reordering array may be of any type, as determined only by newType.

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodType intfn1 = methodType(int.class, int.class);
      MethodType intfn2 = methodType(int.class, int.class, int.class);
      MethodHandle sub = ... (int x, int y) -> (x-y) ...;
      assert(sub.type().equals(intfn2));
      MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
      MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
      assert((int)rsub.invokeExact(1, 100) == 99);
      MethodHandle add = ... (int x, int y) -> (x+y) ...;
      assert(add.type().equals(intfn2));
      MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
      assert(twice.type().equals(intfn1));
      assert((int)twice.invokeExact(21) == 42);
      

      Note: The resulting adapter is never a variable-arity method handle, even if the original target method handle was.

      Parameters:
      target - the method handle to invoke after arguments are reordered
      newType - the expected type of the new method handle
      reorder - an index array which controls the reordering
      Returns:
      a method handle which delegates to the target after it drops unused arguments and moves and/or duplicates the other arguments
      Throws:
      NullPointerException - if any argument is null
      IllegalArgumentException - if the index array length is not equal to the arity of the target, or if any index array element not a valid index for a parameter of newType, or if two corresponding parameter types in target.type() and newType are not identical,
    • constant

      public static MethodHandle constant(Class<?> type, Object value)
      生成一个方法句柄,每次调用时都返回给定的常量值,并将传入的值转换为请求的类型。如果请求的类型是原始类型,则尝试进行扩展原始类型转换,否则尝试进行引用转换。 返回的方法句柄等同于identity(type).bindTo(value)
      参数:
      type - 所需方法句柄的返回类型
      value - 要返回的值
      返回:
      一个给定返回类型和无参数的方法句柄,每次都返回给定值
      抛出:
      NullPointerException - 如果type参数为null
      ClassCastException - 如果值无法转换为所需的返回类型
      IllegalArgumentException - 如果给定类型为void.class
    • identity

      public static MethodHandle identity(Class<?> type)
      生成一个方法句柄,调用时返回其唯一参数。
      参数:
      type - 所需方法句柄的唯一参数和返回值的类型
      返回:
      一个接受并返回给定类型的一元方法句柄
      抛出:
      NullPointerException - 如果参数为null
      IllegalArgumentException - 如果给定类型为void.class
    • zero

      public static MethodHandle zero(Class<?> type)
      生成一个请求的返回类型的常量方法句柄,每次调用时返回该类型的默认值。生成的常量方法句柄不会产生副作用。 返回的方法句柄等同于empty(methodType(type))。它也等同于explicitCastArguments(constant(Object.class, null), methodType(type)),因为explicitCastArgumentsnull转换为默认值。
      参数:
      type - 所需方法句柄的预期返回类型
      返回:
      一个常量方法句柄,不带参数,返回给定类型的默认值(如果类型为void,则返回void)
      抛出:
      NullPointerException - 如果参数为null
      自 JDK 9 起:
      9
      参见:
    • empty

      public static MethodHandle empty(MethodType type)
      生成一个请求类型的方法句柄,忽略任何参数,不执行任何操作,并根据返回类型返回适当的默认值。即返回零原始值、nullvoid。 返回的方法句柄等同于dropArguments(zero(type.returnType()), 0, type.parameterList())
      API 注意:
      给定一个谓词和目标,可以生成一个有用的“if-then”构造,如guardWithTest(pred, target, empty(target.type())
      参数:
      type - 所需方法句柄的类型
      返回:
      一个给定类型的常量方法句柄,返回给定返回类型的默认值
      抛出:
      NullPointerException - 如果参数为null
      自 JDK 9 起:
      9
      参见:
    • insertArguments

      public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values)
      提供一个目标方法句柄,提前绑定一个或多个绑定参数。与绑定参数对应的目标的形式参数称为绑定参数。返回一个新的方法句柄,保存绑定参数。当调用它时,为任何非绑定参数接收参数,将保存的参数绑定到其对应的参数,并调用原始目标。 新方法句柄的类型将从原始目标类型中删除绑定参数的类型,因为新方法句柄将不再需要调用者提供这些参数。 每个给定的参数对象必须与相应的绑定参数类型匹配。如果绑定参数类型是原始类型,则参数对象必须是包装器,并将被拆箱以产生原始值。 pos参数选择要绑定的参数。它可以在零和N-L(包括)之间变化,其中N是目标方法句柄的元数,L是值数组的长度。 注意: 结果适配器永远不会是一个可变元方法句柄,即使原始目标方法句柄是。
      参数:
      target - 插入参数后要调用的方法句柄
      pos - 要插入参数的位置(第一个为零)
      values - 要插入的参数系列
      返回:
      一个插入额外参数后调用原始方法句柄的方法句柄
      抛出:
      NullPointerException - 如果目标或values数组为null
      IllegalArgumentException - 如果pos小于0或大于N - L,其中N是目标方法句柄的元数,L是值数组的长度。
      ClassCastException - 如果参数与相应的绑定参数类型不匹配。
      参见:
    • dropArguments

      public static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes)
      生成一个方法句柄,在调用其他指定的目标方法句柄之前丢弃一些虚拟参数。新方法句柄的类型将与目标的类型相同,只是还包括虚拟参数类型,位于某个给定位置。 pos参数可以在零和N之间变化,其中N是目标的元数。如果pos为零,则虚拟参数将在目标的真实参数之前;如果posN,则它们将在后面。

      示例:

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle cat = lookup().findVirtual(String.class,
        "concat", methodType(String.class, String.class));
      assertEquals("xy", (String) cat.invokeExact("x", "y"));
      MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
      MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
      assertEquals(bigType, d0.type());
      assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
      

      此方法也等同于以下代码:

       dropArguments(target, pos, valueTypes.toArray(new Class[0]))
       
      参数:
      target - 在删除参数后要调用的方法句柄
      pos - 要删除第一个参数的位置(最左边为零)
      valueTypes - 要删除的参数的类型
      返回:
      一个删除给定类型参数后调用原始方法句柄的方法句柄
      抛出:
      NullPointerException - 如果目标为null,或者valueTypes列表或其任何元素为null
      IllegalArgumentException - 如果valueTypes的任何元素为void.class,或者pos为负数或大于目标的元数,或者新方法句柄的类型将具有太多参数
    • dropArguments

      public static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes)
      生成一个方法句柄,在调用其他指定的目标方法句柄之前丢弃一些虚拟参数。新方法句柄的类型将与目标的类型相同,只是还包括虚拟参数类型,位于某个给定位置。 pos参数可以在零和N之间变化,其中N是目标的元数。如果pos为零,则虚拟参数将在目标的真实参数之前;如果posN,则它们将在后面。
      API 注释:
      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle cat = lookup().findVirtual(String.class,
        "concat", methodType(String.class, String.class));
      assertEquals("xy", (String) cat.invokeExact("x", "y"));
      MethodHandle d0 = dropArguments(cat, 0, String.class);
      assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
      MethodHandle d1 = dropArguments(cat, 1, String.class);
      assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
      MethodHandle d2 = dropArguments(cat, 2, String.class);
      assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
      MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
      assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
      

      此方法也等同于以下代码:

       dropArguments(target, pos, Arrays.asList(valueTypes))
       
      参数:
      target - 在删除参数后要调用的方法句柄
      pos - 要删除的第一个参数的位置(最左边为零)
      valueTypes - 要删除的参数的类型
      返回:
      一个方法句柄,它在调用原始方法句柄之前删除给定类型的参数
      抛出:
      NullPointerException - 如果目标为空,或者valueTypes数组或其任何元素为空
      IllegalArgumentException - 如果valueTypes的任何元素是void.class,或者pos为负数或大于目标的参数数量,或者新方法句柄的类型将具有太多参数
    • dropArgumentsToMatch

      public static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos)
      将目标方法句柄调整为匹配给定参数类型列表。如果需要,会添加虚拟参数。在匹配开始之前,可以跳过一些前导参数。目标的参数类型列表中剩余的类型必须是newTypes类型列表的子列表,起始位置为pos。生成的句柄将具有目标句柄的参数类型列表,其中任何不匹配的参数类型(在匹配子列表之前或之后)将按照相应位置插入到目标原始参数中,就像通过dropArguments(MethodHandle, int, Class[])一样。

      生成的句柄将具有与目标句柄相同的返回类型。

      更正式地说,假设有这两个类型列表:

      • 目标句柄具有参数类型列表S..., M...,其中S中的类型数量由skip指示。 M类型是应该与给定类型列表newTypes的一部分匹配的类型。
      • newTypes列表包含类型P..., M..., A...,其中P中的类型数量由pos指示。 M类型恰好是目标句柄的参数类型列表中应该匹配的类型。 A中的类型是在匹配子列表之后找到的其他类型。
      在这些假设的基础上,调用dropArgumentsToMatch的结果将具有参数类型列表S..., P..., M..., A...,其中PA类型将按照dropArguments(MethodHandle, int, Class[])的方式插入。
      API 注释:
      两个参数列表“实质上相同”(即在共同前缀中相同)的方法句柄可以通过两次调用dropArgumentsToMatch相互转换为共同类型,如下所示:
      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      ...
      MethodHandle h0 = constant(boolean.class, true);
      MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
      MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
      MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
      if (h1.type().parameterCount() < h2.type().parameterCount())
          h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0);  // lengthen h1
      else
          h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0);    // lengthen h2
      MethodHandle h3 = guardWithTest(h0, h1, h2);
      assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
      
      参数:
      target - 要适配的方法句柄
      skip - 要忽略的目标参数数量(它们将保持不变)
      newTypes - 要匹配target参数类型列表的类型列表
      pos - 在newTypes中的位置,非跳过的目标参数必须出现的位置
      返回:
      可能经过调整的方法句柄
      抛出:
      NullPointerException - 如果任一参数为空
      IllegalArgumentException - 如果newTypes的任何元素是void.class,或者skip为负数或大于目标的参数数量,或者pos为负数或大于newTypes列表大小,或者newTypes不包含target的非跳过参数类型在位置pos
      自 JDK 版本:
      9
    • dropReturn

      public static MethodHandle dropReturn(MethodHandle target)
      删除目标句柄的返回值(如果有)。返回的方法句柄将具有void返回类型。
      参数:
      target - 要适配的方法句柄
      返回:
      可能经过调整的方法句柄
      抛出:
      NullPointerException - 如果target为空
      自 JDK 版本:
      16
    • filterArguments

      public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters)
      通过对一个或多个参数进行预处理,每个参数都有自己的一元过滤函数,然后用每个预处理参数的结果替换目标参数,从而适配目标方法句柄。预处理由filters数组的元素中指定的一个或多个方法句柄执行。过滤数组的第一个元素对应于目标的pos参数,以此类推。过滤函数按从左到右的顺序调用。

      数组中的空参数被视为恒等函数,并且相应的参数保持不变。(如果数组中没有非空元素,则返回原始目标。)每个过滤器都应用于适配器的相应参数。

      如果过滤器F应用于目标的第N个参数,则F必须是一个接受一个参数的方法句柄。 F的唯一参数的类型将替换结果适配器的目标相应参数类型。 F的返回类型必须与目标的相应参数类型相同。

      如果filters中有与目标不对应的元素(无论是否为空),则会出错。

      示例:

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle cat = lookup().findVirtual(String.class,
        "concat", methodType(String.class, String.class));
      MethodHandle upcase = lookup().findVirtual(String.class,
        "toUpperCase", methodType(String.class));
      assertEquals("xy", (String) cat.invokeExact("x", "y"));
      MethodHandle f0 = filterArguments(cat, 0, upcase);
      assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
      MethodHandle f1 = filterArguments(cat, 1, upcase);
      assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
      MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
      assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
      

      以下是生成适配器的伪代码。在代码中,T表示target和生成适配器的返回类型。 P/pB/b表示在过滤位置pos之前和之后的参数和参数类型。 A[i]/a[i]代表被过滤参数和参数的类型和值;它们还表示filter[i]句柄的返回类型。后者接受类型为V[i]的参数v[i],这也出现在生成适配器的签名中。

      T target(P... p, A[i]... a[i], B... b);
      A[i] filter[i](V[i]);
      T adapter(P... p, V[i]... v[i], B... b) {
        return target(p..., filter[i](v[i])..., b...);
      }
      

      注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。

      参数:
      target - 过滤参数后要调用的方法句柄
      pos - 要过滤的第一个参数的位置
      filters - 要在过滤后的参数上最初调用的方法句柄
      返回:
      包含指定参数过滤逻辑的方法句柄
      抛出:
      NullPointerException - 如果目标为null或者filters数组为null
      IllegalArgumentException - 如果filters的非null元素与目标的相应参数类型不匹配,或者pos+filters.length大于target.type().parameterCount(),或者结果方法句柄的类型会有太多参数
    • collectArguments

      public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter)
      通过使用过滤器(另一个方法句柄)对目标方法句柄进行预处理,使其参数的子序列进行预处理。预处理后的参数将被过滤器函数的结果(如果有的话)替换。然后在修改后的(通常是缩短的)参数列表上调用目标。

      如果过滤器返回一个值,则目标必须接受该值作为其在位置pos的参数,之前和/或之后是未传递给过滤器的任何参数。如果过滤器返回void,则目标必须接受未传递给过滤器的所有参数。不会重新排序参数,并且从过滤器返回的结果将替换(按顺序)最初传递给适配器的参数子序列。

      过滤器的参数类型(如果有)将替换目标的一个参数类型(在位置pos),在生成的适配的方法句柄中。过滤器的返回类型(如果有)必须与目标在位置pos的参数类型相同,并且该目标参数由过滤器的返回值提供。

      在所有情况下,pos必须大于或等于零,并且pos也必须小于或等于目标的参数个数。

      示例:

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle deepToString = publicLookup()
        .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
      
      MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
      assertEquals("[strange]", (String) ts1.invokeExact("strange"));
      
      MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
      assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
      
      MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
      MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
      assertEquals("[top, [up, down], strange]",
                   (String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
      
      MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
      assertEquals("[top, [up, down], [strange]]",
                   (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
      
      MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
      assertEquals("[top, [[up, down, strange], charm], bottom]",
                   (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
      

      以下是生成适配器的伪代码。在代码中,T代表target和生成的适配器的返回类型。 V/v代表filter的返回类型和值,它们也出现在target的签名和参数中,除非VvoidA/aC/c代表在target的签名中在收集位置pos之前和之后的参数类型和值。它们也出现在生成的适配器的签名和参数中,它们围绕B/b,后者代表filter的参数类型和参数(如果有的话)。

      T target(A...,V,C...);
      V filter(B...);
      T adapter(A... a,B... b,C... c) {
        V v = filter(b...);
        return target(a...,v,c...);
      }
      // 如果过滤器没有参数:
      T target2(A...,V,C...);
      V filter2();
      T adapter2(A... a,C... c) {
        V v = filter2();
        return target2(a...,v,c...);
      }
      // 如果过滤器返回void:
      T target3(A...,C...);
      void filter3(B...);
      T adapter3(A... a,B... b,C... c) {
        filter3(b...);
        return target3(a...,c...);
      }
      

      收集适配器collectArguments(mh, 0, coll)等同于首先“折叠”受影响的参数,然后在单独的步骤中将它们删除,如下所示:

      mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //步骤2
      mh = MethodHandles.foldArguments(mh, coll); //步骤1
      
      如果目标方法句柄除了过滤器coll的结果(如果有的话)之外不消耗任何参数,则collectArguments(mh, 0, coll)等同于filterReturnValue(coll, mh)。如果过滤器方法句柄coll消耗一个参数并产生非void结果,则collectArguments(mh, N, coll)等同于filterArguments(mh, N, coll)。其他等价关系是可能的,但需要参数排列。

      注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。

      参数:
      target - 在过滤参数子序列之前要调用的方法句柄
      pos - 要传递给过滤器的第一个适配器参数的位置,和/或接收过滤器结果的目标参数的位置
      filter - 要在参数子序列上调用的方法句柄
      返回:
      包含指定参数子序列过滤逻辑的方法句柄
      抛出:
      NullPointerException - 如果任一参数为null
      IllegalArgumentException - 如果filter的返回类型不是void且与目标的pos参数不同,或者pos不在0和目标的参数个数之间(包括)之间,或者结果方法句柄的类型会有太多参数
      参见:
    • filterReturnValue

      public static MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter)
      通过使用过滤器(另一个方法句柄)对目标方法句柄的返回值(如果有的话)进行后处理,返回过滤器的结果。

      如果目标返回一个值,则过滤器必须接受该值作为其唯一参数。如果目标返回void,则过滤器不得接受任何参数。

      过滤器的返回类型将替换生成的适配方法句柄中目标的返回类型。过滤器的参数类型(如果有)必须与目标的返回类型相同。

      示例:

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle cat = lookup().findVirtual(String.class,
        "concat", methodType(String.class, String.class));
      MethodHandle length = lookup().findVirtual(String.class,
        "length", methodType(int.class));
      System.out.println((String) cat.invokeExact("x", "y")); // xy
      MethodHandle f0 = filterReturnValue(cat, length);
      System.out.println((int) f0.invokeExact("x", "y")); // 2
      

      以下是生成适配器的伪代码。在代码中,T/t代表target的结果类型和值;V代表filter的结果类型;A/a代表target的参数和参数的类型和值,以及生成的适配器。

      T target(A...);
      V filter(T);
      V adapter(A... a) {
        T t = target(a...);
        return filter(t);
      }
      // 如果目标返回void:
      void target2(A...);
      V filter2();
      V adapter2(A... a) {
        target2(a...);
        return filter2();
      }
      // 如果过滤器返回void:
      T target3(A...);
      void filter3(V);
      void adapter3(A... a) {
        T t = target3(a...);
        filter3(t);
      }
      

      注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。

      参数:
      target - 在过滤返回值之前要调用的方法句柄
      filter - 要在返回值上调用的方法句柄
      返回:
      包含指定返回值过滤逻辑的方法句柄
      抛出:
      NullPointerException - 如果任一参数为null
      IllegalArgumentException - 如果filter的参数列表与目标的返回类型不匹配,如上所述
    • foldArguments

      public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner)
      通过预处理一些参数来调整目标方法句柄,然后使用预处理的结果调用目标,插入到原始参数序列中。

      预处理由第二个方法句柄combiner执行。传递给适配器的参数中,前N个参数被复制到combiner,然后调用它。(这里,N被定义为combiner的参数计数。)之后,控制传递到目标,任何来自combiner的结果都会插入到原始N个传入参数之前。

      如果combiner返回一个值,则目标的第一个参数类型必须与combiner的返回类型相同,并且目标的接下来的N个参数类型必须与combiner的参数完全匹配。

      如果combiner返回void,则不会插入任何结果,并且目标的前N个参数类型必须与combiner的参数完全匹配。

      生成的适配器与目标具有相同的类型,除非第一个参数类型与combiner的结果相对应,否则会被删除。

      (请注意,dropArguments可用于删除combiner或目标不希望接收的任何参数。如果一些传入参数仅用于combiner,请考虑使用asCollector,因为这些参数在进入目标时不需要保持活动状态。)

      示例:

      import static java.lang.invoke.MethodHandles.*;
      import static java.lang.invoke.MethodType.*;
      ...
      MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
        "println", methodType(void.class, String.class))
          .bindTo(System.out);
      MethodHandle cat = lookup().findVirtual(String.class,
        "concat", methodType(String.class, String.class));
      assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
      MethodHandle catTrace = foldArguments(cat, trace);
      // 也会打印“boo”:
      assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
      

      这是生成适配器的伪代码。在代码中,T表示target和生成的适配器的结果类型。 V/v表示target的参数和参数的类型和值,这些参数和参数位于折叠位置之前;V也是combiner的结果类型。 A/a表示折叠位置处的N个参数和参数的类型和值。 B/b表示target参数和参数的类型和值,这些参数和参数跟随折叠参数和参数。

      // 在A中有N个参数...
      T target(V, A[N]..., B...);
      V combiner(A...);
      T adapter(A... a, B... b) {
        V v = combiner(a...);
        return target(v, a..., b...);
      }
      // 如果combiner返回void:
      T target2(A[N]..., B...);
      void combiner2(A...);
      T adapter2(A... a, B... b) {
        combiner2(a...);
        return target2(a..., b...);
      }
      

      注意:生成的适配器永远不会是可变参数方法句柄,即使原始目标方法句柄是。

      参数:
      target - 在组合参数后调用的方法句柄
      combiner - 最初在传入参数上调用的方法句柄
      返回:
      包含指定参数折叠逻辑的方法句柄
      抛出:
      NullPointerException - 如果任一参数为null
      IllegalArgumentException - 如果combiner的返回类型非void且与目标的第一个参数类型不同,或者如果目标的初始N个参数类型(跳过与combiner的返回类型匹配的一个)与combiner的参数类型不相同
    • foldArguments

      public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner)
      通过预处理一些参数来调整目标方法句柄,从给定位置开始,并使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。

      此方法与foldArguments(MethodHandle, MethodHandle)密切相关,但允许控制参数列表中折叠发生的位置。控制此位置的参数pos是从零开始的索引。上述方法foldArguments(MethodHandle, MethodHandle)假定位置为0。

      API注释:
      示例:
         import static java.lang.invoke.MethodHandles.*;
         import static java.lang.invoke.MethodType.*;
         ...
         MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
         "println", methodType(void.class, String.class))
         .bindTo(System.out);
         MethodHandle cat = lookup().findVirtual(String.class,
         "concat", methodType(String.class, String.class));
         assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
         MethodHandle catTrace = foldArguments(cat, 1, trace);
         // 也会打印“jum”:
         assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
      

      这是生成适配器的伪代码。在代码中,T表示三个涉及句柄的统一结果类型;A/a表示由test消耗的target参数和参数的类型和值;B/b表示test未消耗的target参数和参数的类型和值。

      // 在A中有N个参数...
      T target(Z..., V, A[N]..., B...);
      V combiner(A...);
      T adapter(Z... z, A... a, B... b) {
        V v = combiner(a...);
        return target(z..., v, a..., b...);
      }
      // 如果combiner返回void:
      T target2(Z..., A[N]..., B...);
      void combiner2(A...);
      T adapter2(Z... z, A... a, B... b) {
        combiner2(a...);
        return target2(z..., a..., b...);
      }
      

      注意:生成的适配器永远不会是可变参数方法句柄,即使原始目标方法句柄是。

      参数:
      target - 在组合参数后调用的方法句柄
      pos - 开始折叠和插入折叠结果的位置;如果这是0,效果与foldArguments(MethodHandle, MethodHandle)相同。
      combiner - 最初在传入参数上调用的方法句柄
      返回:
      包含指定参数折叠逻辑的方法句柄
      抛出:
      NullPointerException - 如果任一参数为null
      IllegalArgumentException - 如果以下两个条件之一成立:(1)combiner的返回类型为非void且与目标签名的位置pos的参数类型不同;(2)目标签名的位置pos处的N个参数类型(跳过与combiner的返回类型匹配的一个)与combiner的参数类型不相同。
      自:
      9
      参见:
    • guardWithTest

      public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
      通过使用测试,一个返回布尔值的方法句柄,保护目标方法句柄来制作一个方法句柄适配器。如果测试失败,则调用一个备用句柄。这三个方法句柄必须具有相同的对应参数和返回类型,除了测试的返回类型必须是布尔值,并且测试允许的参数少于其他两个方法句柄。

      这是生成适配器的伪代码。在代码中,T表示三个涉及句柄的统一结果类型;A/a表示由test消耗的target参数和参数的类型和值;B/b表示test未消耗的target参数和参数的类型和值。

      boolean test(A...);
      T target(A...,B...);
      T fallback(A...,B...);
      T adapter(A... a,B... b) {
        if (test(a...))
          return target(a..., b...);
        else
          return fallback(a..., b...);
      }
      
      请注意,测试参数(伪代码中的a...)不会被测试的执行修改,因此会根据需要从调用者传递给目标或备用。
      参数:
      test - 用于测试的方法句柄,必须返回布尔值
      target - 如果测试通过,则调用的方法句柄
      fallback - 如果测试失败,则调用的方法句柄
      返回:
      包含指定的if/then/else逻辑的方法句柄
      抛出:
      NullPointerException - 如果任何参数为null
      IllegalArgumentException - 如果test不返回布尔值,或者三种方法类型不匹配(将test的返回类型更改为与目标类型匹配)。
    • catchException

      public static MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler)
      创建一个方法句柄,通过在异常处理程序中运行目标方法句柄来适应它。如果目标正常返回,则适配器返回该值。如果抛出与指定类型匹配的异常,则调用回退句柄处理异常,以及原始参数。

      目标和处理程序必须具有相同的对应参数和返回类型,除了处理程序可以省略尾随参数(类似于guardWithTest中的谓词)。此外,处理程序必须具有额外的前导参数exType或超类型。

      以下是生成的适配器的伪代码。在代码中,T表示targethandler的返回类型,以及生成的适配器的返回类型;A/a,是由handler消耗的生成句柄的参数类型和值;B/b,是由handler丢弃的生成句柄的参数类型和值。

      T target(A..., B...);
      T handler(ExType, A...);
      T adapter(A... a, B... b) {
        try {
          return target(a..., b...);
        } catch (ExType ex) {
          return handler(ex, a...);
        }
      }
      
      请注意,保存的参数(伪代码中的a...)不会被目标的执行修改,因此如果调用处理程序,则会从调用者传递给处理程序而不会更改。

      目标和处理程序必须返回相同类型,即使处理程序总是抛出异常。(例如,这可能发生,因为处理程序正在模拟finally子句)。要创建这样一个抛出处理程序,将处理程序创建逻辑与throwException组合,以创建具有正确返回类型的方法句柄。

      参数:
      target - 要调用的方法句柄
      exType - 处理程序将捕获的异常类型
      handler - 如果抛出匹配的异常,则调用的方法句柄
      返回:
      包含指定的try/catch逻辑的方法句柄
      抛出:
      NullPointerException - 如果任何参数为null
      IllegalArgumentException - 如果handler不接受给定的异常类型,或者方法句柄类型在返回类型和对应参数方面不匹配
      参见:
    • throwException

      public static MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType)
      生成一个方法句柄,将抛出给定exType的异常。该方法句柄将接受一个exType的单个参数,并立即将其作为异常抛出。方法类型将名义上指定为returnType的返回。返回类型可以是任何方便的类型:对于方法句柄的行为来说并不重要,因为它永远不会正常返回。
      参数:
      returnType - 所需方法句柄的返回类型
      exType - 所需方法句柄的参数类型
      返回:
      可以抛出给定异常的方法句柄
      抛出:
      NullPointerException - 如果任一参数为null
    • loop

      public static MethodHandle loop(MethodHandle[]... clauses)
      构造一个方法句柄,表示一个循环,其中有几个循环变量在每次迭代时更新和检查。当循环由于其中一个谓词而终止时,将运行相应的终结器并传递循环的结果,这是生成句柄的返回值。

      直观地说,每个循环由一个或多个“子句”组成,每个子句指定一个本地迭代变量和/或一个循环退出。循环的每次迭代按顺序执行每个子句。子句可以选择更新其迭代变量;它还可以选择执行测试和条件循环退出。为了用方法句柄来表达这种逻辑,每个子句将指定最多四个独立的操作:

      • init:在循环执行之前,初始化类型为V的迭代变量v
      • step:当子句执行时,更新迭代变量v的步骤。
      • pred:当子句执行时,执行谓词以测试循环退出。
      • fini:如果子句导致循环退出,则执行终结器以计算循环的返回值。
      在子句顺序中的所有迭代变量类型的完整序列将被标记为(V...)。这些值本身将是(v...)。当我们谈到“参数列表”时,我们通常指的是类型,但在某些上下文中(描述执行时),列表将是实际值。

      根据某些规则,可以省略其中一些子句部分,并在这种情况下提供有用的默认行为。详细描述请参见下文。

      任何地方都是可选的参数:每个子句函数都允许但不是必须接受每个迭代变量v的参数。作为一个例外,init函数不能接受任何v参数,因为在执行init函数时尚未计算这些值。任何子句函数都可以忽略接受其有权接受的任何尾随子序列的参数。实际上,任何子句函数都可以不接受任何参数。

      循环参数:子句函数可以接受其有权接受的所有迭代变量值,这种情况下它也可以接受更多的尾随参数。这些额外的值称为循环参数,其类型和值标记为(A...)(a...)。这些将成为生成的循环句柄的参数,在执行循环时提供。 (由于init函数不接受迭代变量v,因此传递给init函数的任何参数都自动成为循环参数a。)与迭代变量一样,子句函数允许但不是必须接受循环参数。这些循环参数充当整个循环中可见的循环不变值。

      任何地方都可见的参数:每个非init子句函数都允许观察整个循环状态,因为它可以传递当前迭代变量值和传入循环参数的完整列表(v... a...)。init函数可以观察初始的前循环状态,形式为(a...)。大多数子句函数不需要所有这些信息,但它们将被正式连接到这些信息,就好像通过dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>)一样。 更具体地说,我们将使用符号(V*)来表示完整序列(V...)的任意前缀(对于(v*)(A*)(a*)同样如此)。在该符号中,init函数参数列表的一般形式是(A*),非init函数参数列表的一般形式是(V*)(V... A*)

      检查子句结构:给定一组子句,将执行一系列检查和调整以连接循环的所有部分。这些步骤在下面详细说明。在这些步骤中,每次出现“必须”一词时,如果循环组合器的输入不满足所需约束,则将抛出IllegalArgumentException

      有效相同的序列: 定义参数列表A与另一个参数列表B有效相同的,如果AB是相同的,或者如果A更短且与B的一个适当前缀相同。当谈到无序的参数列表集时,我们说整个集合作为一个整体是“有效相同”的,如果集合包含一个最长列表,并且集合中的所有成员都与该最长列表有效相同。例如,任何形式为(V*)的类型序列集合是有效相同的,如果添加更多形式为(V... A*)的序列,情况也是如此。

      步骤0:确定子句结构。

      1. 子句数组(类型为MethodHandle[][])必须为非null,并且至少包含一个元素。
      2. 子句数组不得包含null或超过四个元素的子数组。
      3. 长度小于四个元素的子句将被视为通过附加元素到数组中进行填充。
      4. 所有null的子句将被忽略。
      5. 每个子句被视为一个包含“init”、“step”、“pred”和“fini”四个函数的四元组。

      步骤1A:确定迭代变量类型(V...)

      1. 使用子句的init和step返回类型确定每个子句的迭代变量类型。
      2. 如果两个函数都被省略,则相应子句没有迭代变量(使用void作为类型来指示)。如果其中一个被省略,则另一个的返回类型定义了子句的迭代变量类型。如果两者都给出,则它们的公共返回类型(它们必须相同)定义了子句的迭代变量类型。
      3. 形成返回类型列表(按子句顺序),省略所有void的出现。
      4. 这些类型列表称为“迭代变量类型”((V...))。

      步骤1B:确定循环参数(A...)

      • 检查和收集init函数参数列表(形式为(A*))。
      • 检查和收集step、pred和fini参数列表的后缀,在移除迭代变量类型后(它们必须具有形式(V... A*);仅收集(A*)部分)。
      • 不要从不以所有迭代变量类型开头的step、pred和fini参数列表中收集后缀。(这些类型将在步骤2中检查,以及所有子句函数类型。)
      • 省略的子句函数将被忽略。(等效地,它们被视为具有空参数列表。)
      • 所有收集到的参数列表必须是有效相同的。
      • 最长参数列表(必然是唯一的)称为“外部参数列表”((A...))。
      • 如果没有这样的参数列表,则外部参数列表被视为空序列。
      • 由迭代变量类型后跟外部参数类型组成的列表称为“内部参数列表”。

      步骤1C:确定循环返回类型。

      1. 检查fini函数的返回类型,忽略省略的fini函数。
      2. 如果没有fini函数,则循环返回类型为void
      3. 否则,fini函数的公共返回类型R(它们的返回类型必须相同)定义了循环返回类型。

      步骤1D:检查其他类型。

      1. 必须至少有一个非省略的pred函数。
      2. 每个非省略的pred函数必须具有boolean返回类型。

      步骤2:确定参数列表。

      1. 生成的循环句柄的参数列表将是外部参数列表(A...)
      2. init函数的参数列表将调整为外部参数列表。(注意,它们的参数列表已经有效相同于此列表。)
      3. 每个非省略的非init(step、pred和fini)函数的参数列表必须有效相同于内部参数列表(V... A...)

      步骤3:填充省略的函数。

      1. 如果省略了init函数,则对应子句的迭代变量类型使用默认值
      2. 如果省略了step函数,则使用子句的迭代变量类型的恒等函数;在前面子句的非void迭代变量的恒等函数参数之前插入被丢弃的参数。(这将使循环变量成为本地循环不变量。)
      3. 如果省略了pred函数,则使用一个常量true函数。(这将使循环继续进行,就该子句而言。请注意,在这种情况下,相应的fini函数是无法到达的。)
      4. 如果省略了fini函数,则使用默认值作为循环返回类型。

      步骤4:填充缺失的参数类型。

      1. 此时,每个init函数的参数列表都有效相同于外部参数列表(A...),但某些列表可能较短。对于参数列表较短的每个init函数,填充列表末尾。
      2. 此时,每个非init函数的参数列表都有效相同于内部参数列表(V... A...),但某些列表可能较短。对于参数列表较短的每个非init函数,填充列表末尾。
      3. 参数列表通过删除未使用的尾随参数进行填充。

      最终观察。

      1. 经过这些步骤,所有子句都已通过提供省略的函数和参数进行调整。
      2. 所有init函数具有一个公共参数类型列表(A...),最终的循环句柄也将具有该参数类型列表。
      3. 所有fini函数具有一个公共返回类型R,最终的循环句柄也将具有该返回类型。
      4. 所有非init函数具有一个公共参数类型列表(V... A...),其中包括(非void)迭代变量V和循环参数。
      5. 每对init和step函数在其返回类型V上达成一致。
      6. 每个非init函数将能够观察所有迭代变量的当前值(v...)
      7. 每个函数将能够观察所有循环参数的传入值(a...)

      示例。 由于上述步骤1A的结果,loop组合器具有以下属性:

      • 给定N个子句Cn = {null, Sn, Pn},其中n = 1..N
      • 假设谓词处理器Pn要么是null,要么没有参数。(只有一个Pn必须是非null的。)
      • 假设步骤处理器Sn具有签名(B1..BX)Rn,对于某个常数X>=N
      • 假设Q是非void类型Rn的计数,(V1...VQ)是这些类型的序列。
      • 必须满足Vn == Bn,对于n = 1..min(X,Q)
      • 参数类型Vn将被解释为循环局部状态元素(V...)
      • 任何剩余的类型BQ+1..BX(如果Q)将确定生成的循环处理器的参数类型(A...)
      在这个例子中,循环处理器参数(A...)是从步骤函数派生的,如果大部分循环计算发生在步骤中,这是很自然的。对于一些循环,计算的负担可能在谓词函数中最重,因此谓词函数可能需要接受循环参数值。对于具有复杂退出逻辑的循环,结束函数可能需要接受循环参数,同样对于具有复杂进入逻辑的循环,初始化函数将需要额外的参数。出于这些原因,确定这些参数的规则尽可能对称,跨所有子句部分。一般来说,循环参数作为整个循环中的共同不变值,而迭代变量作为共同变化值,或者(如果没有步骤函数)作为内部循环不变临时变量。

      循环执行。

      1. 当循环被调用时,循环输入值被保存在本地变量中,以便传递给每个子句函数。这些本地变量是循环不变的。
      2. 每个初始化函数按子句顺序执行(传递外部参数(a...)),非void值被保存(作为迭代变量(v...))到本地变量中。这些本地变量将是循环变化的(除非它们的步骤行为像恒等函数一样,如上所述)。
      3. 所有函数执行(除了初始化函数)将传递内部参数列表,包括非void迭代值(v...)(按子句顺序)然后是循环输入(a...)(按参数顺序)。
      4. 然后执行步骤和谓词函数,按子句顺序执行(步骤在谓词之前),直到谓词函数返回false
      5. 来自步骤函数调用的非void结果用于更新循环变量序列(v...)中相应的值。更新后的值立即对所有后续函数调用可见。
      6. 如果谓词函数返回false,则调用相应的结束函数,并将结果值(类型为R)作为整个循环的返回值。
      7. 如果所有谓词函数始终返回true,则永远不会调用任何结束函数,并且循环除非抛出异常否则无法退出。

      使用提示。

      • 虽然每个步骤函数将接收所有循环变量的当前值,但有时步骤函数只需要观察自己变量的当前值。在这种情况下,步骤函数可能需要显式地丢弃所有前面的循环变量。这将需要提及它们的类型,如dropArguments(step, 0, V0.class, ...)这样的表达式。
      • 循环变量不需要变化;它们可以是循环不变的。一个子句可以通过适当的初始化函数创建一个循环不变量,该函数没有步骤、谓词或结束函数。这可能对将传入的循环参数"连接"到相邻循环变量的步骤或谓词函数中是有用的。
      • 如果一些子句函数是实例上的虚拟方法,实例本身可以方便地放置在初始不变循环"变量"中,使用类似new MethodHandle[]{identity(ObjType.class)}的初始子句。在这种情况下,实例引用将是第一个迭代变量值,并且很容易使用虚拟方法作为子句部分,因为所有这些方法将采用与该值匹配的前导实例引用。

      以下是生成的循环处理器的伪代码。如上所述,Vv表示循环变量的类型和值;Aa表示传递给整个循环的参数;R是所有最终器的常见结果类型,以及循环的结果。

      V... init...(A...);
      boolean pred...(V..., A...);
      V... step...(V..., A...);
      R fini...(V..., A...);
      R loop(A... a) {
        V... v... = init...(a...);
        for (;;) {
          for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
            v = s(v..., a...);
            if (!p(v..., a...)) {
              return f(v..., a...);
            }
          }
        }
      }
      
      请注意,参数类型列表(V...)(A...)已扩展到其完整长度,即使个别子句函数可能忽略其中的一些。如上所述,缺少的参数将被填充,就像通过dropArgumentsToMatch(MethodHandle, int, List, int)一样。
      API注释:
      示例:
      // 阶乘函数的迭代实现作为循环句柄
      static int one(int k) { return 1; }
      static int inc(int i, int acc, int k) { return i + 1; }
      static int mult(int i, int acc, int k) { return i * acc; }
      static boolean pred(int i, int acc, int k) { return i < k; }
      static int fin(int i, int acc, int k) { return acc; }
      // 假设MH_one、MH_inc、MH_mult、MH_pred和MH_fin是上述方法的句柄
      // 计数器的空初始化器,应初始化为0
      MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
      MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
      MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
      assertEquals(120, loop.invoke(5));
      
      相同的示例,省略参数并使用组合器:
      // 阶乘函数的简化实现作为循环句柄
      static int inc(int i) { return i + 1; } // 省略acc, k
      static int mult(int i, int acc) { return i * acc; } //省略k
      static boolean cmp(int i, int k) { return i < k; }
      // 假设MH_inc、MH_mult和MH_cmp是上述方法的句柄
      // 计数器的空初始化器,应初始化为0
      MethodHandle MH_one = MethodHandles.constant(int.class, 1);
      MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // 省略acc
      MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // 省略i
      MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
      MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
      MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
      assertEquals(720, loop.invoke(6));
      
      使用辅助对象保存循环参数的类似示例:
      // 阶乘函数的基于实例的实现作为循环句柄
      static class FacLoop {
        final int k;
        FacLoop(int k) { this.k = k; }
        int inc(int i) { return i + 1; }
        int mult(int i, int acc) { return i * acc; }
        boolean pred(int i) { return i < k; }
        int fin(int i, int acc) { return acc; }
      }
      // 假设MH_FacLoop是构造函数的句柄
      // 假设MH_inc、MH_mult、MH_pred和MH_fin是上述方法的句柄
      // 计数器的空初始化器,应初始化为0
      MethodHandle MH_one = MethodHandles.constant(int.class, 1);
      MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
      MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
      MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
      MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
      assertEquals(5040, loop.invoke(7));
      
      参数:
      clauses - 符合上述规则的MethodHandle数组的数组(4元组)。
      返回:
      体现由参数定义的循环行为的方法句柄。
      抛出:
      IllegalArgumentException - 如果违反上述任何约束。
      自:
      9
      参见:
    • whileLoop

      public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body)
      构造一个while循环,包括初始化器、主体和谓词。这是对通用循环组合器的便利包装。

      pred句柄描述了循环条件;body描述了循环主体。通过此方法得到的循环将在每次迭代中首先评估谓词,然后执行其主体(如果谓词评估为true)。一旦谓词评估为false(在这种情况下不会执行主体),循环将终止。

      init句柄描述了一个额外可选的循环局部变量的初始值。在每次迭代中,如果存在此循环局部变量,它将被传递给body并更新为其调用返回的值。循环执行的结果将是额外循环局部变量(如果存在)的最终值。

      这些参数句柄的规则如下:

      • body句柄不能为null;其类型必须为(V A...)V的形式,其中V为非void,否则为(A...)void。(在void情况下,我们将void类型V分配给名称V,并且我们将以理解一个void类型V从参数列表中被静默删除的方式写(V A...)V,留下(A...)V。)
      • 主体的参数列表(V A...)称为内部参数列表。它将约束其他循环部分的参数列表。
      • 如果迭代变量类型V从内部参数列表中删除,则产生的较短列表(A...)称为外部参数列表
      • 如果主体返回类型V为非void,则确定循环的一个额外状态变量的类型V。主体必须同时接受并返回此类型V的值。
      • 如果init不为null,则必须具有返回类型V。其参数列表(某种(A*)形式)必须与外部参数列表(A...) 有效地相同
      • 如果initnull,则循环变量将被初始化为其默认值
      • pred句柄不能为null。其返回类型必须为boolean。其参数列表(空或(V A*)形式)必须与内部参数列表有效地相同。

      生成的循环句柄的结果类型和参数签名如下确定:

      • 循环句柄的结果类型是主体的结果类型V
      • 循环句柄的参数类型是外部参数列表(A...)的类型。

      以下是生成的循环句柄的伪代码。在代码中,V/v表示唯一循环变量的类型/值以及循环的结果类型;A/a表示传递给循环的参数的类型/值。

      V init(A...);
      boolean pred(V, A...);
      V body(V, A...);
      V whileLoop(A... a...) {
        V v = init(a...);
        while (pred(v, a...)) {
          v = body(v, a...);
        }
        return v;
      }
      
      API注释:
      示例:
      // 作为循环句柄实现列表的zip函数
      static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
      static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
      static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
        zip.add(a.next());
        zip.add(b.next());
        return zip;
      }
      // 假设MH_initZip、MH_zipPred和MH_zipStep是上述方法的句柄
      MethodHandle loop = MethodHandles.whileLoop(MH_initZip, MH_zipPred, MH_zipStep);
      List<String> a = Arrays.asList("a", "b", "c", "d");
      List<String> b = Arrays.asList("e", "f", "g", "h");
      List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
      assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
      
      ,此方法的实现可以表达如下:
      MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
          MethodHandle fini = (body.type().returnType() == void.class
                              ? null : identity(body.type().returnType()));
          MethodHandle[]
              checkExit = { null, null, pred, fini },
              varBody   = { init, body };
          return loop(checkExit, varBody);
      }
      
      参数:
      init - 可选的初始化器,提供循环变量的初始值。可以为null,暗示默认初始值。其他约束请参见上文。
      pred - 循环条件,不得为null。其结果类型必须为boolean。其他约束请参见上文。
      body - 循环主体,不得为null。它控制循环的参数和结果类型。其他约束请参见上文。
      返回:
      实现描述参数所描述的while循环的方法句柄。
      异常:
      IllegalArgumentException - 如果违反参数规则。
      NullPointerException - 如果predbodynull
      自 JDK 9 起:
      9
      参见:
    • doWhileLoop

      public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred)
      构造一个do-while循环,包括初始化器、主体和谓词。这是对通用循环组合器的便利包装。

      pred句柄描述了循环条件;body描述了循环主体。通过此方法得到的循环将在每次迭代中首先执行其主体,然后评估谓词。在执行主体后,一旦谓词评估为false,循环将终止。

      init句柄描述了一个额外可选的循环局部变量的初始值。在每次迭代中,如果存在此循环局部变量,它将被传递给body并更新为其调用返回的值。循环执行的结果将是额外循环局部变量(如果存在)的最终值。

      这些参数句柄的规则如下:

      • body句柄不能为null;其类型必须为(V A...)V的形式,其中V为非void,否则为(A...)void。(在void情况下,我们将void类型V分配给名称V,并且我们将以理解一个void类型V从参数列表中被静默删除的方式写(V A...)V,留下(A...)V。)
      • 主体的参数列表(V A...)称为内部参数列表。它将约束其他循环部分的参数列表。
      • 如果迭代变量类型V从内部参数列表中删除,则产生的较短列表(A...)称为外部参数列表
      • 主体返回类型V,如果非void,则确定循环的一个额外状态变量的类型V。主体必须同时接受并返回此类型V的值。
      • 如果init不为null,则必须具有返回类型V。其参数列表(某种(A*)形式)必须与外部参数列表(A...) 有效地相同
      • 如果initnull,则循环变量将被初始化为其默认值
      • pred句柄不能为null。其返回类型必须为boolean。其参数列表(空或(V A*)形式)必须与内部参数列表有效地相同。

      生成的循环句柄的结果类型和参数签名如下确定:

      • 循环句柄的结果类型是主体的结果类型V
      • 循环句柄的参数类型是外部参数列表(A...)的类型。

      以下是生成的循环句柄的伪代码。在代码中,V/v表示唯一循环变量的类型/值以及循环的结果类型;A/a表示传递给循环的参数的类型/值。

      V init(A...);
      boolean pred(V, A...);
      V body(V, A...);
      V doWhileLoop(A... a...) {
        V v = init(a...);
        do {
          v = body(v, a...);
        } while (pred(v, a...));
        return v;
      }
      
      API注释:
      示例:
      // int i = 0; while (i < limit) { ++i; } return i; => limit
      static int zero(int limit) { return 0; }
      static int step(int i, int limit) { return i + 1; }
      static boolean pred(int i, int limit) { return i < limit; }
      // 假设MH_zero、MH_step和MH_pred是上述方法的句柄
      MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
      assertEquals(23, loop.invoke(23));
      
      , 此方法的实现可以表达如下:
      MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
          MethodHandle fini = (body.type().returnType() == void.class
                              ? null : identity(body.type().returnType()));
          MethodHandle[] clause = { init, body, pred, fini };
          return loop(clause);
      }
      
      参数:
      init - 可选的初始化器,提供循环变量的初始值。可以为null,表示默认初始值。其他约束请参见上文。
      body - 循环体,不可为null。它控制循环的参数和结果类型。其他约束请参见上文。
      pred - 循环条件,不可为null。其结果类型必须为boolean。其他约束请参见上文。
      返回值:
      实现描述的while循环的方法句柄。
      抛出:
      IllegalArgumentException - 如果违反参数规则。
      NullPointerException - 如果predbodynull
      自版本:
      9
      参见:
    • countedLoop

      public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body)
      构造一个运行给定迭代次数的循环。这是通用循环组合器的便利包装器。

      迭代次数由iterations句柄的评估结果确定。循环计数器i是一个额外的循环迭代变量,类型为int。它将被初始化为0,并在每次迭代中递增1。

      如果body句柄返回非void类型V,则还会存在一个该类型的额外循环迭代变量。此变量将使用可选的init句柄进行初始化,或者如果该句柄为null,则使用类型V默认值进行初始化。

      在每次迭代中,迭代变量将传递给body句柄的调用。从循环体返回的非void值(类型为V)将更新额外的迭代变量。循环句柄执行的结果将是该变量的最终V值(如果没有V变量,则为void)。

      以下规则适用于参数句柄:

      • iterations句柄不能为null,必须返回类型为int,在参数类型列表中称为I
      • body句柄不能为null;其类型必须为(V I A...)V的形式,其中V为非void,否则为(I A...)void。(在void情况下,我们将V类型为void,并且我们将(V I A...)V写为理解为void类型V在参数列表中被静默删除,留下(I A...)V。)
      • 循环体的参数列表(V I A...)贡献给一个称为内部参数列表的类型列表。它将约束其他循环部分的参数列表。
      • 作为特例,如果循环体仅贡献VI类型,没有额外的A类型,则内部参数列表将扩展为iterations句柄的参数类型A...
      • 如果迭代变量类型(V I)从内部参数列表中删除,则产生的较短列表(A...)称为外部参数列表
      • 循环体的返回类型V,如果非void,将确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回该类型V的值。
      • 如果init不为null,则其必须具有返回类型V。其参数列表(某种形式为(A*))必须有效相同于外部参数列表(A...)
      • 如果initnull,则循环变量将被初始化为其默认值
      • iterations的参数列表(某种形式为(A*))必须有效相同于外部参数列表(A...)

      生成的循环句柄的结果类型和参数签名确定如下:

      • 循环句柄的结果类型是循环体的结果类型V
      • 循环句柄的参数类型是外部参数列表(A...)中的类型。

      以下是生成的循环句柄的伪代码。在代码中,V/v表示第二个循环变量的类型/值以及循环的结果类型;A.../a...表示传递给循环的参数。

      int iterations(A...);
      V init(A...);
      V body(V, int, A...);
      V countedLoop(A... a...) {
        int end = iterations(a...);
        V v = init(a...);
        for (int i = 0; i < end; ++i) {
          v = body(v, i, a...);
        }
        return v;
      }
      
      API注释:
      具有完全符合体方法的示例:
      // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
      // => 一个众所周知的主题的变体
      static String step(String v, int counter, String init) { return "na " + v; }
      // 假设MH_step是上述方法的句柄
      MethodHandle fit13 = MethodHandles.constant(int.class, 13);
      MethodHandle start = MethodHandles.identity(String.class);
      MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step);
      assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
      
      , 具有最简单体方法类型的示例,并将迭代次数传递给循环调用:
      // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
      // => 一个众所周知的主题的变体
      static String step(String v, int counter ) { return "na " + v; }
      // 假设MH_step是上述方法的句柄
      MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
      MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
      MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i) -> "na " + v
      assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
      
      , 将迭代次数、要附加的字符串和要附加的字符串作为循环参数的示例:
      // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
      // => 一个众所周知的主题的变体
      static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; }
      // 假设MH_step是上述方法的句柄
      MethodHandle count = MethodHandles.identity(int.class);
      MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
      MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i, _, pre, _) -> pre + " " + v
      assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
      
      , 说明使用dropArgumentsToMatch(MethodHandle, int, List, int)来强制执行循环类型的示例:
      // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
      // => 一个众所周知的主题的变体
      static String step(String v, int counter, String pre) { return pre + " " + v; }
      // 假设MH_step是上述方法的句柄
      MethodType loopType = methodType(String.class, String.class, int.class, String.class);
      MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class),    0, loopType.parameterList(), 1);
      MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
      MethodHandle body  = MethodHandles.dropArgumentsToMatch(MH_step,                              2, loopType.parameterList(), 0);
      MethodHandle loop = MethodHandles.countedLoop(count, start, body);  // (v, i, pre, _, _) -> pre + " " + v
      assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
      
      , 该方法的实现可以表达如下:
      MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
          return countedLoop(empty(iterations.type()), iterations, init, body);
      }
      
      参数:
      iterations - 用于返回此循环应运行的迭代次数的非null句柄。句柄的结果类型必须为int。有关其他约束,请参见上文。
      init - 可选的初始化器,提供循环变量的初始值。可以为null,表示默认初始值。有关其他约束,请参见上文。
      body - 循环体,不得为null。它控制标准情况下的循环参数和结果类型。它必须接受自己的返回类型(如果非void)加上一个int参数(用于计数),并且可以接受任意数量的其他类型。有关其他约束,请参见上文。
      返回:
      代表循环的方法句柄。
      抛出:
      NullPointerException - 如果iterationsbody句柄中的任一句柄为null
      IllegalArgumentException - 如果任何参数违反上述规则。
      自:
      9
      参见:
    • countedLoop

      public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body)
      构造一个在一系列数字上计数的循环。这是通用循环组合器的便利包装器。

      循环计数器i是一个类型为int的循环迭代变量。startend句柄确定循环计数器的起始(包括)和结束(不包括)值。循环计数器将从start句柄的评估返回的int值初始化,并以从end返回的值(不包括)以步宽1运行。

      如果body句柄返回非void类型V,则该类型的一个前导循环迭代变量也存在。此变量使用可选的init句柄初始化,或者如果该句柄为null,则使用类型V默认值初始化。

      在每次迭代中,迭代变量将传递给body句柄的调用。从循环体返回的非void值(类型为V)将更新前导迭代变量。循环句柄执行的结果将是该变量的最终V值(如果没有V变量,则为void)。

      以下规则适用于参数句柄:

      • startend句柄不能为null,并且必须都返回公共类型int,在参数类型列表中称为I
      • body句柄不能为null;其类型必须为(V I A...)V的形式,其中V为非void,否则为(I A...)void。(在void情况下,我们将void类型V分配给名称V,并且我们将(V I A...)V写为理解到void类型V已从参数列表中静默删除,留下(I A...)V。)
      • 循环体的参数列表(V I A...)贡献给称为内部参数列表的类型列表。它将约束其他循环部分的参数列表。
      • 作为特例,如果循环体仅贡献VI类型,没有额外的A类型,则内部参数列表将由end句柄的参数类型A...扩展。
      • 如果迭代变量类型(V I)从内部参数列表中删除,则结果较短的列表(A...)称为外部参数列表
      • 循环体的返回类型V(如果非void)确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回此类型V的值。
      • 如果init为非null,则其必须具有返回类型V。其参数列表(某种形式(A*))必须有效相同于外部参数列表(A...)
      • 如果initnull,则循环变量将初始化为其默认值
      • start的参数列表(某种形式(A*))必须有效相同于外部参数列表(A...)
      • 同样,end的参数列表必须有效相同于外部参数列表。

      生成的循环句柄的结果类型和参数签名如下确定:

      • 循环句柄的结果类型是循环体的结果类型V
      • 循环句柄的参数类型是外部参数列表(A...)中的类型。

      以下是生成的循环句柄的伪代码。在代码中,V/v表示第二个循环变量的类型/值以及循环的结果类型;A.../a...表示传递给循环的参数。

      int start(A...);
      int end(A...);
      V init(A...);
      V body(V, int, A...);
      V countedLoop(A... a...) {
        int e = end(a...);
        int s = start(a...);
        V v = init(a...);
        for (int i = s; i < e; ++i) {
          v = body(v, i, a...);
        }
        return v;
      }
      
      API注释:
      此方法的实现可以表达如下:
      MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
          MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
          // 假设MH_increment和MH_predicate是指向具有以下语义的内部实现方法的句柄:
          // MH_increment: (int limit, int counter) -> counter + 1
          // MH_predicate: (int limit, int counter) -> counter < limit
          Class<?> counterType = start.type().returnType();  // int
          Class<?> returnType = body.type().returnType();
          MethodHandle incr = MH_increment, pred = MH_predicate, retv = null;
          if (returnType != void.class) {  // 忽略V变量
              incr = dropArguments(incr, 1, returnType);  // (limit, v, i) => (limit, i)
              pred = dropArguments(pred, 1, returnType);  // 同上
              retv = dropArguments(identity(returnType), 0, counterType); // 忽略limit
          }
          body = dropArguments(body, 0, counterType);  // 忽略limit变量
          MethodHandle[]
              loopLimit  = { end, null, pred, retv }, // limit = end(); i < limit || return v
              bodyClause = { init, body },            // v = init(); v = body(v, i)
              indexVar   = { start, incr };           // i = start(); i = i + 1
          return loop(loopLimit, bodyClause, indexVar);
      }
      
      参数:
      start - 用于返回循环计数器起始值的非null句柄,必须为int类型。其他约束请参见上文。
      end - 用于返回循环计数器结束值的非null句柄(循环将运行到end-1)。结果类型必须为int。其他约束请参见上文。
      init - 可选的初始化器,提供循环变量的初始值。可以为null,表示默认初始值。其他约束请参见上文。
      body - 循环体,不得为null。在标准情况下,它控制循环参数和结果类型(详细信息请参见上文)。如果返回类型为非void,则必须接受自身的返回类型(如果非void)加上一个int参数(用于计数器),并且可以接受任意数量的其他类型。其他约束请参见上文。
      返回:
      代表循环的方法句柄。
      抛出:
      NullPointerException - 如果startendbody句柄中有任何一个为null
      IllegalArgumentException - 如果任何参数违反上述规则。
      自版本:
      9
      参见:
    • iteratedLoop

      public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body)
      构造一个循环,范围为由Iterator<T>生成的值。这是通用循环组合器的便捷包装器。

      迭代器本身将由iterator句柄的评估确定。它产生的每个值将存储在类型为T的循环迭代变量中。

      如果body句柄返回非void类型V,则还将存在一个前导循环迭代变量。此变量将使用可选的init句柄进行初始化,或者如果该句柄为null,则使用类型V默认值

      在每次迭代中,迭代变量将传递给body句柄的调用。从body返回的非void值(类型为V)将更新前导迭代变量。循环句柄执行的结果将是该变量的最终V值(如果没有V变量,则为void)。

      以下规则适用于参数句柄:

      • body句柄不得为null;其类型必须为(V T A...)V形式,其中V为非void,否则为(T A...)void。(在void情况下,我们将类型void分配给名称V,并且我们将在理解中写(V T A...)V,理解为void类型V在参数列表中被静默删除,留下(T A...)V。)
      • 循环体的参数列表(V T A...)将贡献到称为内部参数列表的类型列表中。它将约束其他循环部分的参数列表。
      • 作为特例,如果循环体仅贡献VT类型,没有额外的A类型,则内部参数列表将扩展为iterator句柄的A...参数类型;如果它为null,则添加单个类型Iterable并构成A...列表。
      • 如果迭代变量类型(V T)从内部参数列表中删除,则生成的较短列表(A...)称为外部参数列表
      • 循环体返回类型V(如果非void)确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回此类型V的值。
      • 如果init为非null,则其必须具有返回类型V。其参数列表(某种形式为(A*))必须有效地相同于外部参数列表(A...)
      • 如果initnull,则循环变量将初始化为其默认值
      • 如果iterator句柄为非null,则其必须具有返回类型java.util.Iterator或其子类型。在执行循环时,假定迭代器产生的值可以转换为类型T
      • nulliterator的参数列表(某种形式为(A*))必须有效地与外部参数列表(A...)相同。
      • 如果iteratornull,它将默认为一个行为类似于Iterable.iterator()的方法句柄。在这种情况下,内部参数列表(V T A...)必须至少有一个A类型,并且默认迭代器句柄参数将调整为接受前导A类型,就好像通过asType转换方法一样。前导A类型必须为Iterable或其子类型。在循环构造时执行此转换步骤,不得抛出WrongMethodTypeException

      类型T可以是原始类型或引用类型。由于方法句柄表示中的Iterator<T>类型被擦除为原始类型Iterator,因此iteratedLoop组合器将body的前导参数类型调整为Object,就好像通过asType转换方法一样。因此,如果在执行循环时出现错误类型的迭代器,则可能会由MethodHandle.asType(MethodType)执行的动态转换导致运行时异常。

      生成的循环句柄的结果类型和参数签名如下确定:

      • 循环句柄的结果类型为循环体的结果类型V
      • 循环句柄的参数类型为外部参数列表中的类型(A...)

      以下是生成的循环句柄的伪代码。在代码中,V/v表示循环变量的类型/值以及循环的结果类型;T/t表示循环迭代的结构元素的类型,A.../a...表示传递给循环的参数。

      Iterator<T> iterator(A...);  // 默认为Iterable::iterator
      V init(A...);
      V body(V,T,A...);
      V iteratedLoop(A... a...) {
        Iterator<T> it = iterator(a...);
        V v = init(a...);
        while (it.hasNext()) {
          T t = it.next();
          v = body(v, t, a...);
        }
        return v;
      }
      
      API注释:
      示例:
      // 从列表中获取迭代器
      static List<String> reverseStep(List<String> r, String e) {
        r.add(0, e);
        return r;
      }
      static List<String> newArrayList() { return new ArrayList<>(); }
      // 假设MH_reverseStep和MH_newArrayList是上述方法的句柄
      MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
      List<String> list = Arrays.asList("a", "b", "c", "d", "e");
      List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
      assertEquals(reversedList, (List<String>) loop.invoke(list));
      
      , 此方法的实现大致可以表达如下:
      MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
          // 假设MH_next、MH_hasNext、MH_startIter是Iterator/Iterable方法的句柄
          Class<?> returnType = body.type().returnType();
          Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
          MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
          MethodHandle retv = null, step = body, startIter = iterator;
          if (returnType != void.class) {
              // 首先简单处理: 在 (I V A...) 中,去掉 I 得到 V
              retv = dropArguments(identity(returnType), 0, Iterator.class);
              // body类型签名 (V T A...),内部循环类型 (I V A...)
              step = swapArguments(body, 0, 1);  // 交换 V <-> T
          }
          if (startIter == null)  startIter = MH_getIter;
          MethodHandle[]
              iterVar    = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext())
              bodyClause = { init, filterArguments(step, 0, nextVal) };  // v = body(v, t, a)
          return loop(iterVar, bodyClause);
      }
      
      参数:
      iterator - 一个可选的句柄,用于返回开始循环的迭代器。如果非null,则句柄必须返回Iterator或其子类型。其他约束请参见上文。
      init - 可选的初始化器,提供循环变量的初始值。可以为null,表示默认初始值。其他约束请参见上文。
      body - 循环体,不得为null。在标准情况下,它控制循环参数和结果类型(详细信息请参见上文)。它必须接受自身的返回类型(如果非void)加上一个T参数(用于迭代值),并且可以接受任意数量的其他类型。其他约束请参见上文。
      返回:
      体现迭代循环功能的方法句柄。
      异常:
      NullPointerException - 如果body句柄为null
      IllegalArgumentException - 如果任何参数违反上述要求。
      自 JDK 版本:
      9
    • tryFinally

      public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup)
      创建一个方法句柄,通过将其包装在try-finally块中来适应target方法句柄。另一个方法句柄cleanup代表finally块的功能。在执行target句柄期间抛出的任何异常都将传递给cleanup句柄。除非cleanup句柄首先抛出异常,否则将重新抛出异常。cleanup句柄执行的返回值将是try-finally句柄执行的结果。

      cleanup句柄将被传递一个或两个额外的前导参数。第一个是在执行target句柄期间抛出的异常,或者如果没有抛出异常,则为null。第二个是target句柄的执行结果,或者如果它抛出异常,则作为占位符提供所需类型的null、零或false值。如果target句柄具有void返回类型,则第二个参数不存在。(请注意,除了参数类型转换外,组合器通过省略相应的悖论参数来在参数列表中表示void值,而不是插入null或零值。)

      targetcleanup句柄必须具有相同的对应参数和返回类型,除了cleanup句柄可以省略尾随参数。此外,cleanup句柄必须具有一个或两个额外的前导参数:

      • 一个Throwable,将携带target句柄抛出的异常(如果有);和
      • targetcleanup的返回类型相同的参数,将携带target句柄的执行结果。如果target返回void,则不会出现第二个参数。

      生成的适配器的伪代码如下所示。在代码中,V表示try/finally结构的结果类型;A/a表示由清理句柄消耗的结果句柄的类型和值;B/b表示由清理句柄丢弃的结果句柄的类型和值。

      V target(A..., B...);
      V cleanup(Throwable, V, A...);
      V adapter(A... a, B... b) {
        V result = (V的零值);
        Throwable throwable = null;
        try {
          result = target(a..., b...);
        } catch (Throwable t) {
          throwable = t;
          throw t;
        } finally {
          result = cleanup(throwable, result, a...);
        }
        return result;
      }
      

      请注意,保存的参数(伪代码中的a...)不能被目标的执行修改,因此如果调用了清理句柄,则将其无修改地传递给清理句柄。

      目标和清理必须返回相同的类型,即使清理总是抛出异常。要创建这样一个抛出异常的清理,将清理逻辑与throwException组合,以创建具有正确返回类型的方法句柄。

      请注意,tryFinally永远不会将异常转换为正常返回。在极少数情况下,必须以这种方式转换异常时,首先使用catchException(MethodHandle, Class, MethodHandle)包装目标以捕获传出异常,然后再包装为tryFinally

      建议cleanup的第一个参数类型声明为Throwable而不是更窄的子类型。这确保cleanup将始终使用target抛出的任何异常调用。声明更窄的类型可能导致try-finally句柄抛出ClassCastException,如果target抛出的异常类型不能分配给cleanup的第一个参数类型。请注意,VirtualMachineErrorLinkageErrorRuntimeException的各种异常类型原则上可以由几乎任何类型的Java代码抛出,而仅捕获(例如)IOException的finally子句将在ClassCastException后面掩盖任何其他异常。

      参数:
      target - 要包装在try块中的执行句柄。
      cleanup - 在finally块中调用的句柄。
      返回:
      体现由这两个参数组成的try-finally块的方法句柄。
      异常:
      NullPointerException - 如果任何参数为null
      IllegalArgumentException - 如果cleanup不接受所需的前导参数,或者如果方法句柄类型在返回类型和对应的尾随参数上不匹配
      自 JDK 版本:
      9
      参见:
    • tableSwitch

      public static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets)
      创建一个表切换方法句柄,可用于根据给定的目标索引(称为选择器)切换一组目标方法句柄。

      对于选择器值为n,其中n在范围[0, N)内,且N为目标方法句柄的数量,表切换方法句柄将从目标方法句柄列表中调用第n个目标方法句柄。

      对于不在范围[0, N)内的选择器值,表切换方法句柄将调用给定的回退方法句柄。

      传递给此方法的所有方法句柄必须具有相同的类型,另外要求前导参数为int类型。前导参数表示选择器。

      类型中存在的任何尾随参数也将出现在返回的表切换方法句柄上。将分配给这些参数的任何参数将与选择器值一起转发到调用选定方法句柄时。

      API注释:
      示例: 每个案例都会丢弃它们所给定的selector值,并接受一个额外的String参数,该参数被连接(使用String.concat(String))到每个案例的特定常量标签字符串上:
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      MethodHandle caseMh = lookup.findVirtual(String.class, "concat",
              MethodType.methodType(String.class, String.class));
      caseMh = MethodHandles.dropArguments(caseMh, 0, int.class);
      
      MethodHandle caseDefault = MethodHandles.insertArguments(caseMh, 1, "default: ");
      MethodHandle case0 = MethodHandles.insertArguments(caseMh, 1, "case 0: ");
      MethodHandle case1 = MethodHandles.insertArguments(caseMh, 1, "case 1: ");
      
      MethodHandle mhSwitch = MethodHandles.tableSwitch(
          caseDefault,
          case0,
          case1
      );
      
      assertEquals("default: data", (String) mhSwitch.invokeExact(-1, "data"));
      assertEquals("case 0: data", (String) mhSwitch.invokeExact(0, "data"));
      assertEquals("case 1: data", (String) mhSwitch.invokeExact(1, "data"));
      assertEquals("default: data", (String) mhSwitch.invokeExact(2, "data"));
      
      参数:
      fallback - 当选择器不在范围[0, N)内时调用的回退方法句柄。
      targets - 目标方法句柄数组。
      返回:
      表切换方法句柄。
      抛出:
      NullPointerException - 如果fallbacktargets数组或targets数组的任何元素为null
      IllegalArgumentException - 如果targets数组为空,如果回退句柄或任何目标句柄的前导参数不是int,或者如果回退句柄和所有目标句柄的类型不相同。
    • memorySegmentViewVarHandle

      public static VarHandle memorySegmentViewVarHandle(ValueLayoutPREVIEW layout)
      memorySegmentViewVarHandle是Java平台的预览API。
      仅当启用预览功能时,程序才能使用memorySegmentViewVarHandle
      预览功能可能会在将来的版本中被移除,或升级为Java平台的永久功能。
      创建一个变量句柄对象,可用于根据提供的值布局对给定字节偏移量处的memory segment预览进行解引用。

      提供的布局指定了与返回的变量句柄相关联的载体类型预览字节大小预览字节对齐预览字节顺序预览

      与返回的变量句柄相关联的坐标类型列表为(MemorySegment, long),其中long坐标类型对应于给定内存段坐标中的字节偏移量。因此,返回的变量句柄访问给定内存段中偏移量处的字节,将字节组合到变量句柄类型的值中,或从变量句柄类型的值中提取字节。此外,访问操作将遵守提供的布局中表达的字节顺序和对齐约束。

      例如,考虑由以下方式构造的GroupLayout预览实例表示的内存布局:

          GroupLayout seq = java.lang.foreign.MemoryLayout.structLayout(
                  MemoryLayout.paddingLayout(4),
                  ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
          );
      
      要访问名为value的成员布局,我们可以构造一个内存段视图变量句柄,如下所示:
          VarHandle handle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); //(MemorySegment, long) -> int
          handle = MethodHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
      
      API注释:
      结果变量句柄具有特定的访问模式限制,这些限制适用于所有内存段视图变量句柄。内存段视图变量句柄关联有一个访问大小S和一个对齐约束B(均以字节表示)。我们说一个内存访问操作是完全对齐的,如果它发生在一个与对齐约束SB都兼容的内存地址A上。如果访问是完全对齐的,则支持以下访问模式,并且保证支持原子访问:
      • 对于所有T,读写访问模式都受支持,但在32位平台上,longdoublegetset访问模式除外。
      • intlongfloatdoubleMemorySegment预览的原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他类型的访问模式。)
      • intlongMemorySegment预览的数值原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他数值类型的访问模式。)
      • intlongMemorySegment预览的位原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他数值类型的访问模式。)
      如果TfloatdoubleMemorySegment预览,则原子更新访问模式将使用它们的位表示进行值比较(参见Float.floatToRawIntBits(float)Double.doubleToRawLongBits(double)MemorySegment.address()预览)。

      另外,如果内存访问操作是部分对齐的,即发生在仅与对齐约束B兼容的内存地址A上;在这种情况下,除了getset访问模式之外的任何访问都将导致IllegalStateException。如果访问是部分对齐的,则原子访问仅保证相对于AS的最大二的幂的GCD。

      在所有其他情况下,我们说内存访问操作是不对齐的;在这种情况下,无论使用的访问模式如何,都会抛出IllegalStateException

      最后,如果TMemorySegment,则所有写访问模式都会抛出IllegalArgumentException,除非要写入的值是一个本机预览内存段。

      参数:
      layout - 要获取内存访问句柄的值布局。
      返回:
      新的内存段视图变量句柄。
      抛出:
      NullPointerException - 如果layoutnull
      自版本:
      19
      参见:
    • filterValue

      public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget)
      filterValue是Java平台的预览API。
      仅当启用预览功能时,程序才能使用filterValue
      预览功能可能会在将来的版本中被移除,或升级为Java平台的永久功能。
      通过使用一对过滤函数对目标变量句柄进行预处理,使其能够适应。

      例如,在结果变量句柄上调用 VarHandle.set(Object...) 时,传入值(类型为 T,其中 T 是第一个过滤函数的最后一个参数类型)将使用第一个过滤器进行处理,然后传递给目标变量句柄。相反,在结果变量句柄上调用 VarHandle.get(Object...) 时,从目标变量句柄获取的返回值(类型为 T,其中 T 是第二个过滤函数的最后一个参数类型)将使用第二个过滤器进行处理并返回给调用者。更高级的访问模式类型,例如 VarHandle.AccessMode.COMPARE_AND_EXCHANGE 可能同时应用两个过滤器。

      为了使装箱和拆箱过滤器形成良好的形式,它们的类型必须为 (A... , S) -> T(A... , T) -> S,其中 T 是目标变量句柄的类型。如果是这种情况,生成的变量句柄将具有类型 S,并将包含额外的坐标 A...(这些坐标将附加到目标变量句柄的坐标)。

      如果装箱和拆箱过滤器在调用时抛出任何已检查异常,则生成的变量句柄将抛出一个 IllegalStateException

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见 VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 目标变量句柄
      filterToTarget - 用于将某种类型 S 转换为 target 类型的过滤器
      filterFromTarget - 用于将 target 类型转换为某种类型 S 的过滤器
      返回:
      一个适配器变量句柄,接受新类型,执行提供的装箱/拆箱转换。
      抛出:
      IllegalArgumentException - 如果 filterFromTargetfilterToTarget 不是形式良好的,即它们的类型不是 (A... , S) -> T(A... , T) -> S,其中 T 是目标变量句柄的类型,或者确定 filterFromTargetfilterToTarget 在调用时抛出任何已检查异常。
      NullPointerException - 如果任何参数为 null
      自:
      19
    • filterCoordinates

      public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters)
      filterCoordinates 是 Java 平台的预览 API。
      仅当启用预览功能时,程序才能使用 filterCoordinates
      预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。
      通过使用一元过滤函数对目标变量句柄的传入坐标值进行预处理,使其能够适应。

      例如,在结果变量句柄上调用 VarHandle.get(Object...) 时,从位置 pos 开始的传入坐标值(类型为 C1, C2 ... Cn,其中 C1, C2 ... Cn 是一元过滤函数的返回类型)将被转换为新值(类型为 S1, S2 ... Sn,其中 S1, S2 ... Sn 是一元过滤函数的参数类型),然后传递(连同任何未被调整的坐标)给目标变量句柄。

      为了使坐标过滤器形成良好的形式,它们的类型必须为 S1 -> T1, S2 -> T1 ... Sn -> Tn,其中 T1, T2 ... Tn 是从位置 pos 开始的目标变量句柄的坐标类型。

      如果任何过滤器在调用时抛出已检查异常,则生成的变量句柄将抛出一个 IllegalStateException

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见 VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 目标变量句柄
      pos - 要转换的第一个坐标的位置
      filters - 用于转换从位置 pos 开始的坐标的一元函数
      返回:
      一个适配器变量句柄,接受新的坐标类型,将提供的转换应用于新的坐标值。
      抛出:
      IllegalArgumentException - 如果 filters 中的句柄不是形式良好的,即它们的类型不是 S1 -> T1, S2 -> T2, ... Sn -> Tn,其中 T1, T2 ... Tn 是从位置 pos 开始的目标变量句柄的坐标类型,如果 pos 不在 0 到目标变量句柄坐标数量(包括)之间,或者如果提供的过滤器比从 pos 开始的实际坐标类型的数量还要多,或者确定任何过滤器在调用时抛出任何已检查异常。
      NullPointerException - 如果任何参数为 nullfilters 包含 null
      自:
      19
    • insertCoordinates

      public static VarHandle insertCoordinates(VarHandle target, int pos, Object... values)
      insertCoordinates 是 Java 平台的预览 API。
      仅当启用预览功能时,程序才能使用 insertCoordinates
      预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。
      为目标变量句柄提供一个或多个提前绑定的坐标,作为变量句柄调用的一部分。因此,生成的变量句柄将具有比目标变量句柄更少的坐标类型。

      例如,在结果变量句柄上调用 VarHandle.get(Object...) 时,传入的坐标值将与绑定的坐标值连接,然后传递给目标变量句柄。

      为了使绑定的坐标形成良好的形式,它们的类型必须为 T1, T2 ... Tn ,其中 T1, T2 ... Tn 是从位置 pos 开始的目标变量句柄的坐标类型。

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见 VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 插入绑定坐标后要调用的变量句柄
      pos - 要插入的第一个坐标的位置
      values - 要插入的一系列绑定坐标
      返回:
      一个适配器变量句柄,插入额外的坐标,然后调用目标变量句柄
      抛出:
      IllegalArgumentException - 如果 pos 不在 0 到目标变量句柄坐标数量(包括)之间,或者如果提供的值比从 pos 开始的实际坐标类型的数量还要多。
      ClassCastException - 如果 values 中的绑定坐标不是形式良好的,即它们的类型不是 T1, T2 ... Tn ,其中 T1, T2 ... Tn 是从位置 pos 开始的目标变量句柄的坐标类型。
      NullPointerException - 如果任何参数为 nullvalues 包含 null
      自:
      19
    • permuteCoordinates

      public static VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder)
      permuteCoordinates 是 Java 平台的预览 API。
      仅当启用预览功能时,程序才能使用 permuteCoordinates
      预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。
      通过重新排列目标变量句柄的坐标值,使其新坐标与提供的坐标匹配,提供一个适配目标变量句柄的变量句柄。

      给定的数组控制重新排序。将传入坐标的数量(值为 newCoordinates.size())称为 #I,将传出坐标的数量(与目标变量句柄关联的坐标数量)称为 #O。然后,重新排序数组的长度必须为 #O,每个元素必须是小于 #I 的非负数。对于小于 #O 的每个 N,第 N 个传出坐标将从第 I 个传入坐标中取出,其中 Ireorder[N]

      不会应用坐标值转换。由 newCoordinates 确定的每个传入坐标的类型必须与目标变量句柄中相应传出坐标的类型相同。

      重新排序数组不需要指定实际的排列。如果其索引在数组中出现多次,则传入坐标将被复制,如果其索引未在数组中出现,则传入坐标将被丢弃。

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见 VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 重新排序坐标后要调用的变量句柄
      newCoordinates - 新的坐标类型
      reorder - 控制重新排序的索引数组
      返回:
      一个适配器变量句柄,用于重新排列传入的坐标值,然后调用目标变量句柄
      抛出:
      IllegalArgumentException - 如果索引数组的长度不等于目标变量句柄的坐标数量,或者如果任何索引数组元素不是newCoordinates的有效索引,或者目标变量句柄和newCoordinates中的两个对应坐标类型不相同。
      NullPointerException - 如果任何参数是nullnewCoordinates包含null
      自:
      19
    • collectCoordinates

      public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter)
      collectCoordinates是Java平台的预览API。
      只有在启用预览功能时,程序才能使用collectCoordinates
      预览功能可能会在将来的版本中删除,或升级为Java平台的永久功能。
      通过使用过滤器(方法句柄)对目标变量句柄的子序列坐标值进行预处理,从而适应目标变量句柄。预处理后的坐标将被过滤函数的结果(如果有的话)替换,然后在修改后的(通常是缩短的)坐标列表上调用目标变量句柄。

      如果R是过滤器的返回类型(不能为void),则目标变量句柄必须接受类型为R的值作为其在位置pos的坐标,之前和/或之后是未传递给过滤器的任何坐标。不会重新排序坐标,并且从适配器传递给目标的坐标子序列中的整个坐标子序列将被过滤器的返回值替换(按顺序)。

      过滤器的参数类型(如果有)将替换目标变量句柄的零个或一个坐标类型,在位置pos处,在生成的适配变量句柄中。过滤器的返回类型必须与目标变量句柄在位置pos处的坐标类型相同,并且目标变量句柄的坐标由过滤器的返回值提供。

      如果在调用任何过滤器时抛出已检查异常,则生成的变量句柄将抛出一个IllegalStateException

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 在过滤坐标后要调用的变量句柄
      pos - 要过滤的坐标的位置
      filter - 过滤器方法句柄
      返回:
      一个适配器变量句柄,用于在调用目标变量句柄之前过滤传入的坐标值
      抛出:
      IllegalArgumentException - 如果filter的返回类型为void,或者与目标变量句柄的pos坐标不同,如果pos不在0和目标变量句柄坐标数量之间(包括)之间,如果生成的变量句柄的类型将具有太多坐标,或者确定filter会抛出任何已检查异常。
      NullPointerException - 如果任何参数是null
      自:
      19
    • dropCoordinates

      public static VarHandle dropCoordinates(VarHandle target, int pos, Class<?>... valueTypes)
      dropCoordinates是Java平台的预览API。
      只有在启用预览功能时,程序才能使用dropCoordinates
      预览功能可能会在将来的版本中删除,或升级为Java平台的永久功能。
      返回一个变量句柄,将在委托给目标变量句柄之前丢弃一些虚拟坐标。因此,生成的变量句柄将具有比目标变量句柄更多的坐标类型。

      pos参数可以在零和N之间取值,其中N是目标变量句柄的坐标类型的数量。如果pos为零,则虚拟坐标将位于目标的真实参数之前;如果posN,则它们将在其后。

      生成的变量句柄将具有与目标变量句柄相同的访问模式(参见VarHandle.AccessMode)和原子访问保证。

      参数:
      target - 在丢弃虚拟坐标后要调用的变量句柄
      pos - 要丢弃的第一个坐标的位置(最左边为零)
      valueTypes - 要丢弃的坐标的类型
      返回:
      一个适配器变量句柄,用于在调用目标变量句柄之前丢弃一些虚拟坐标
      抛出:
      IllegalArgumentException - 如果pos不在0和目标变量句柄坐标数量之间(包括)。
      NullPointerException - 如果任何参数是nullvalueTypes包含null
      自:
      19