- 有助于为方法和字段创建方法句柄的查找方法。
- 组合器方法,将预先存在的方法句柄组合或转换为新的方法句柄。
- 用于创建模拟其他常见 JVM 操作或控制流模式的方法句柄的其他工厂方法。
IllegalArgumentException。
- 自从:
- 1.7
-
内部类总结
内部类 -
方法总结
修饰符和类型方法描述static MethodHandlearrayConstructor(Class<?> arrayClass) 生成一个方法句柄来构造所需类型的数组,就像通过anewarray字节码一样。static MethodHandlearrayElementGetter(Class<?> arrayClass) 生成一个方法句柄,提供对数组元素的读取访问权限,就像通过aaload字节码一样。static MethodHandlearrayElementSetter(Class<?> arrayClass) 生成一个方法句柄,提供对数组元素的写访问权限,就像通过astore字节码一样。static VarHandlearrayElementVarHandle(Class<?> arrayClass) 生成一个 VarHandle,可以访问类型为arrayClass的数组的元素。static MethodHandlearrayLength(Class<?> arrayClass) 生成一个返回数组长度的方法句柄,就像由arraylength字节码一样。static VarHandlebyteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) 生成一个 VarHandle,可以访问byte[]数组的元素,就像它是不同的原始数组类型一样,例如int[]或long[]。static VarHandlebyteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) 生成一个 VarHandle,允许访问ByteBuffer的元素,将其视为与byte不同的基本组件类型的元素数组,例如int[]或long[]。static MethodHandlecatchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler) 通过在异常处理程序中运行它来创建一个适应目标方法句柄的方法句柄。static <T> TclassData(MethodHandles.Lookup caller, String name, Class<T> type) 返回与给定caller查找对象或null的查找类关联的 class data。static <T> TclassDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index) static MethodHandlecollectArguments(MethodHandle target, int pos, MethodHandle filter) 通过使用过滤器(另一个方法句柄)预处理其参数的子序列来调整目标方法句柄。static VarHandlecollectCoordinates(VarHandle target, int pos, MethodHandle filter) 预览。通过使用过滤器(方法句柄)预处理其坐标值的子序列来调整目标变量句柄。static MethodHandle生成请求的返回类型的方法句柄,每次调用时返回给定的常量值。static MethodHandlecountedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) 构造一个运行给定迭代次数的循环。static MethodHandlecountedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) 构造一个对一定范围内的数字进行计数的循环。static MethodHandledoWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) 从初始值设定项、主体和谓词构造一个do-while循环。static MethodHandledropArguments(MethodHandle target, int pos, Class<?>... valueTypes) 生成一个方法句柄,该句柄将在调用其他指定的参数之前丢弃一些伪参数目标方法句柄。static MethodHandledropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) 生成一个方法句柄,该句柄将在调用其他指定的参数之前丢弃一些伪参数目标方法句柄。static MethodHandledropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos) 调整目标方法句柄以匹配给定的参数类型列表。static VarHandledropCoordinates(VarHandle target, int pos, Class<?>... valueTypes) 预览。返回一个 var 句柄,它将在委托给目标 var 句柄之前丢弃一些虚拟坐标。static MethodHandledropReturn(MethodHandle target) 删除目标句柄的返回值(如果有)。static MethodHandleempty(MethodType type) 生成请求类型的方法句柄,该句柄忽略任何参数,不执行任何操作,并根据返回类型返回合适的默认值。static MethodHandleexactInvoker(MethodType type) 生成一个特殊的 invoker method handle 可用于调用给定类型的任何方法句柄,就像通过invokeExact一样。static MethodHandleexplicitCastArguments(MethodHandle target, MethodType newType) 生成一个方法句柄,它通过成对参数和返回类型转换使给定方法句柄的类型适应新类型。static MethodHandlefilterArguments(MethodHandle target, int pos, MethodHandle... filters) 通过预处理一个或多个参数来调整目标方法句柄,每个参数都有自己的一元过滤函数,然后调用目标,每个预处理参数替换为相应过滤函数的结果。static VarHandlefilterCoordinates(VarHandle target, int pos, MethodHandle... filters) 预览。通过使用一元过滤函数预处理传入的坐标值来调整目标变量句柄。static MethodHandlefilterReturnValue(MethodHandle target, MethodHandle filter) 通过使用过滤器(另一个方法句柄)后处理其返回值(如果有)来调整目标方法句柄。static VarHandlefilterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) 预览。过滤器函数预处理传入和传出值来调整目标 var 句柄。static MethodHandlefoldArguments(MethodHandle target, int pos, MethodHandle combiner) 通过预处理它的一些参数来调整目标方法句柄,从给定位置开始,然后使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。static MethodHandlefoldArguments(MethodHandle target, MethodHandle combiner) 通过预处理它的一些参数来调整目标方法句柄,然后使用预处理的结果调用目标,将其插入到原始参数序列中。static MethodHandleguardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) 通过用测试保护它来创建一个适应目标方法句柄的方法句柄,一个boolean方法句柄。static MethodHandle生成一个方法句柄,该句柄在调用时返回其唯一参数。static MethodHandleinsertArguments(MethodHandle target, int pos, Object... values) 在方法句柄调用之前为目标方法句柄提供一个或多个bound arguments。static VarHandleinsertCoordinates(VarHandle target, int pos, Object... values) 预览。在调用 var 句柄之前,为目标 var 句柄提供一个或多个 bound coordinates。static MethodHandleinvoker(MethodType type) 产生一个特殊的 invoker method handle 可用于调用与给定类型兼容的任何方法句柄,就像通过invoke一样。static MethodHandleiteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) 构造一个循环,其范围超过Iterator<T>产生的值。static MethodHandles.Lookuplookup()返回具有完整功能的lookup object来模拟调用者所有支持的字节码行为。static MethodHandleloop(MethodHandle[]... clauses) 构造一个表示循环的方法句柄,其中包含多个循环变量,这些循环变量在每次迭代时更新和检查。static VarHandlestatic MethodHandlepermuteArguments(MethodHandle target, MethodType newType, int... reorder) 通过重新排序参数,生成一个方法句柄,该方法句柄将给定方法句柄的调用序列调整为新类型。static VarHandlepermuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder) 预览。提供一个 var 句柄,它通过重新排列目标 var 句柄的坐标值来调整它们,以便新坐标与提供的坐标相匹配。static MethodHandles.LookupprivateLookupIn(Class<?> targetClass, MethodHandles.Lookup caller) static MethodHandles.Lookup返回最低信任度的lookup object。static <T extends Member>
TreflectAs(Class<T> expected, MethodHandle target) 对 直接方法句柄 执行未经检查的“破解”。static MethodHandlespreadInvoker(MethodType type, int leadingArgCount) 生成一个方法句柄,它将调用给定type的任何方法句柄,给定数量的尾随参数替换为单个尾随Object[]数组。static MethodHandletableSwitch(MethodHandle fallback, MethodHandle... targets) 创建一个表切换方法句柄,可用于根据给定的目标索引(称为选择器)切换一组目标方法句柄。static MethodHandlethrowException(Class<?> returnType, Class<? extends Throwable> exType) 生成一个方法句柄,它将抛出给定exType的异常。static MethodHandletryFinally(MethodHandle target, MethodHandle cleanup) 通过将target方法句柄包装在try-finally块中来创建一个方法句柄。static MethodHandlevarHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type) 生成一个特殊的 invoker method handle,它可用于在关联访问模式类型与给定类型兼容的任何 VarHandle 上调用签名多态访问模式方法。static MethodHandlevarHandleInvoker(VarHandle.AccessMode accessMode, MethodType type) 生成一个特殊的 invoker method handle,它可用于在关联访问模式类型与给定类型兼容的任何 VarHandle 上调用签名多态访问模式方法。static MethodHandlewhileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) 从初始值设定项、主体和谓词构造一个while循环。static MethodHandle生成所请求返回类型的常量方法句柄,每次调用时返回该类型的默认值。
-
方法详情
-
lookup
返回具有完整功能的lookup object来模拟调用者所有支持的字节码行为。这些功能包括对调用者的 完全权限访问。查找对象上的工厂方法可以为调用者可以通过字节码访问的任何成员创建直接方法句柄,包括受保护和私有字段和方法。此查找对象由原始查找类创建,并设置了ORIGINAL位。这个查找对象是一个capability,可以委托给受信任的代理。不要将它存储在不受信任的代码可以访问它的地方。此方法对调用者敏感,这意味着它可能会向不同的调用者返回不同的值。如果
MethodHandles.lookup是从堆栈上没有调用者框架的上下文调用的(例如,当直接从 JNI 附加线程调用时),则抛出IllegalCallerException。要在此类上下文中获取lookup object,请使用将隐式标识为调用方的辅助类,或使用publicLookup()获取低权限查找。- 返回:
- 此方法的调用者的查找对象,带有 原来的 和 完全权限访问 。
- 抛出:
IllegalCallerException- 如果堆栈上没有调用者帧。
-
publicLookup
返回最低信任度的lookup object。查找具有UNCONDITIONAL模式。它只能用于为无条件导出的包中的公共类的公共成员创建方法句柄。- API 注意:
-
Object 的使用是常规的,并且因为查找模式是有限的,所以没有提供对 Object 内部、它的包或它的模块的特殊访问。此公共查找对象或其他具有
UNCONDITIONAL模式的查找对象假定可读性。因此,查找类不用于确定查找上下文。Discussion: 可以使用
publicLookup().in(C.class)形式的表达式将查找类更改为任何其他类C。公共查找对象始终受 安全经理检查 约束。此外,它无法访问 调用者敏感方法 。 - 返回:
- 最低限度信任的查找对象
-
privateLookupIn
public static MethodHandles.Lookup privateLookupIn(Class <?> targetClass, MethodHandles.Lookup caller) throws IllegalAccessException 返回目标类上的lookup对象以模拟所有支持的字节码行为,包括 私人访问 。返回的查找对象可以提供对模块和包中的类以及这些类的成员的访问,在 Java 访问控制的正常规则之外,而不是符合模块化 deep reflection 更宽松的规则。当且仅当以下所有条件都是
true时,允许在模块M1中指定为Lookup对象的调用者对模块M2和目标类的包进行深度反射:- 如果有security manager,调用它的
checkPermission方法检查ReflectPermission("suppressAccessChecks"),必须正常返回。 - 调用者查找对象必须有 完全权限访问 。具体来说:
- 目标类必须是适当的类,而不是原始类或数组类。 (因此,
M2是明确定义的。) - 如果调用方模块
M1与目标模块M2不同,则以下两项必须为真:
如果违反了上述任何检查,此方法将失败并出现异常。
否则,如果
M1和M2是同一个模块,则此方法返回LookupontargetClasswith 完全权限访问 withnullprevious lookup class。否则,
M1和M2是两个不同的模块。此方法在targetClass上返回一个Lookup,它将调用者的查找类记录为具有PRIVATE访问权限但没有MODULE访问权限的新的先前查找类。生成的
Lookup对象没有ORIGINAL访问权限。- 参数:
targetClass- 目标类caller- 调用者查找对象- 返回:
- 目标类的查找对象,具有私有访问权限
- 抛出:
IllegalArgumentException- 如果targetClass是原始类型或 void 或数组类NullPointerException- 如果targetClass或caller是nullSecurityException- 如果安全经理拒绝IllegalAccessException- 如果上面指定的任何其他访问检查失败- 自从:
- 9
- 参见:
- 如果有security manager,调用它的
-
classData
public static <T> T classData(MethodHandles.Lookup caller, String name, Class <T> type) throws IllegalAccessException 返回与给定caller查找对象或null的查找类关联的 class data。可以通过调用
Lookup::defineHiddenClassWithClassData创建具有类数据的隐藏类。如果给定的caller查找对象的查找类尚未初始化,则此方法将导致执行静态类初始化程序。Lookup::defineHiddenClass创建的隐藏类和非隐藏类没有类数据。如果在这些类的查找对象上调用此方法,则返回null。- API 注意:
-
此方法可以称为动态计算常量的引导方法。框架可以使用类数据创建隐藏类,例如可以是
Class或MethodHandle对象。类数据只能由原始调用者创建的查找对象访问,但同一嵌套中的其他成员无法访问。如果框架通过类数据将安全敏感对象传递给隐藏类,建议将类数据的值加载为动态计算的常量,而不是将类数据存储在其他同组成员可以访问的私有静态字段中。 - 类型参数:
T- 将类数据对象转换为的类型- 参数:
caller- 描述执行操作的类的查找上下文(通常由 JVM 堆叠)name- 必须是ConstantDescs.DEFAULT_NAME("_")type- 类数据的类型- 返回:
-
类数据的值(如果存在于查找类中);否则
null - 抛出:
IllegalArgumentException- 如果名称不是"_"IllegalAccessException- 如果查找上下文没有 原来的 访问权限ClassCastException- 如果类数据不能转换为给定的typeNullPointerException- 如果caller或type参数是null- 看Java 虚拟机规范:
-
5.5 初始化
- 自从:
- 16
- 参见:
-
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- 如果caller或type参数是null;或者如果拆箱操作失败,因为给定索引处的元素是null- 自从:
- 16
- 参见:
-
reflectAs
对 直接方法句柄 执行未经检查的“破解”。结果就好像用户获得了一个足以破解目标方法句柄的查找对象,在目标上调用Lookup.revealDirect来获取它的符号引用,然后调用MethodHandleInfo.reflectAs来解析对成员的符号引用。如果有安全管理器,它的
checkPermission方法将以ReflectPermission("suppressAccessChecks")权限调用。- 类型参数:
T- 所需的结果类型,Member或子类型- 参数:
expected- 表示所需结果类型的类对象Ttarget- 破解符号引用组件的直接方法句柄- 返回:
- 对方法、构造方法或字段对象的引用
- 抛出:
SecurityException- 如果调用者无权调用setAccessibleNullPointerException- 如果任一参数是nullIllegalArgumentException- 如果目标不是直接方法句柄ClassCastException- 如果成员不是预期类型- 自从:
- 1.8
-
arrayConstructor
生成一个方法句柄来构造所需类型的数组,就像通过anewarray字节码一样。方法句柄的返回类型将是数组类型。其唯一参数的类型将是int,它指定数组的大小。如果使用负数组大小调用返回的方法句柄,将抛出
NegativeArraySizeException。- 参数:
arrayClass- 数组类型- 返回:
- 可以创建给定类型数组的方法句柄
- 抛出:
NullPointerException- 如果参数是nullIllegalArgumentException- 如果arrayClass不是数组类型- 看Java 虚拟机规范:
-
6.5
anewarray指令
- 自从:
- 9
- 参见:
-
arrayLength
生成一个返回数组长度的方法句柄,就像由arraylength字节码一样。方法句柄的类型将int作为返回类型,其唯一参数将是数组类型。如果使用
null数组引用调用返回的方法句柄,将抛出NullPointerException。- 参数:
arrayClass- 数组类型- 返回:
- 一个方法句柄,它可以检索给定数组类型的数组的长度
- 抛出:
NullPointerException- 如果参数是nullIllegalArgumentException- 如果 arrayClass 不是数组类型- 看Java 虚拟机规范:
-
6.5
arraylength指令
- 自从:
- 9
-
arrayElementGetter
生成一个方法句柄,提供对数组元素的读取访问权限,就像通过aaload字节码一样。方法句柄的类型将具有数组元素类型的返回类型。它的第一个参数是数组类型,第二个参数是int。调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用是
null,将抛出NullPointerException;如果索引为负或大于或等于数组的长度,将抛出ArrayIndexOutOfBoundsException。- 参数:
arrayClass- 数组类型- 返回:
- 可以从给定数组类型加载值的方法句柄
- 抛出:
NullPointerException- 如果参数为空IllegalArgumentException- 如果 arrayClass 不是数组类型- 看Java 虚拟机规范:
-
6.5
aaload指令
-
arrayElementSetter
生成一个方法句柄,提供对数组元素的写访问权限,就像通过astore字节码一样。方法句柄的类型将具有 void 返回类型。它的最后一个参数将是数组的元素类型。第一个和第二个参数将是数组类型和 int。调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用是
null,将抛出NullPointerException;如果索引为负或大于或等于数组的长度,将抛出ArrayIndexOutOfBoundsException。- 参数:
arrayClass- 数组的类- 返回:
- 可以将值存储到数组类型中的方法句柄
- 抛出:
NullPointerException- 如果参数为空IllegalArgumentException- 如果 arrayClass 不是数组类型- 看Java 虚拟机规范:
-
6.5
aastore指令
-
arrayElementVarHandle
生成一个 VarHandle,可以访问类型为arrayClass的数组的元素。 VarHandle 的变量类型是arrayClass的组件类型,坐标类型列表是(arrayClass, int),其中int坐标类型对应于作为数组索引的参数。在以下情况下,不支持返回的 VarHandle 的某些访问模式:
- 如果组件类型不是
byte、short、char、int、long、float或double,则不支持数字原子更新访问模式。 - 如果组件类型不是
boolean、byte、short、char、int或long,则不支持按位原子更新访问模式。
如果组件类型是
float或double,则数字和原子更新访问模式使用它们的按位表示比较值(分别参见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。在使用此类值执行比较和设置或比较和交换操作时应小心,因为操作可能会意外失败。有许多可能的 NaN 值在 Java 中被认为是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 为空IllegalArgumentException- 如果 arrayClass 不是数组类型- 自从:
- 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、float和double。如果索引小于
0或大于byte[]数组长度减去T的大小(以字节为单位),则访问给定索引处的字节将导致ArrayIndexOutOfBoundsException。T在索引处访问字节可能对齐或未对齐,相对于与数组和索引关联的底层内存地址,比如A。如果访问未对齐,则对get和set访问模式以外的任何访问都将导致IllegalStateException。在这种情况下,原子访问只能保证A的 GCD 和T的大小(以字节为单位)的最大幂次方。如果访问对齐,则支持以下访问模式并保证支持原子访问:- 所有
T的读写访问模式,32位平台上long和double的访问模式get和set除外。 int、long、float或double的原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他类型。)int和long的数字原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)int和long的按位原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)
可以在不对特定数组进行操作的情况下为
byte[]数组确定未对齐的访问,从而保证原子性。给定index、T及其对应的盒装类型T_BOX,可以按如下方式确定未对齐:int sizeOfT = T_BOX.BYTES; // size in bytes of T int misalignedAtZeroIndex = ByteBuffer.wrap(new byte[0]). alignmentOffset(0, sizeOfT); int misalignedAtIndex = (misalignedAtZeroIndex + index) % sizeOfT; boolean isMisaligned = misalignedAtIndex != 0;如果变量类型是
float或double,则原子更新访问模式使用它们的按位表示比较值(分别参见Float.floatToRawIntBits(float)和Double.doubleToRawLongBits(double))。- 参数:
viewArrayClass- 视图数组类,组件类型为TbyteOrder- 视图数组元素的字节顺序,存储在底层byte数组中- 返回:
-
一个 VarHandle,可以访问
byte[]数组的元素,被视为与视图数组类的组件类型相对应的元素 - 抛出:
NullPointerException- 如果 viewArrayClass 或 byteOrder 为空IllegalArgumentException- 如果 viewArrayClass 不是数组类型UnsupportedOperationException- 如果不支持将 viewArrayClass 的组件类型作为变量类型- 自从:
- 9
- 所有
-
byteBufferViewVarHandle
public static VarHandle byteBufferViewVarHandle(Class <?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException 生成一个 VarHandle,允许访问ByteBuffer的元素,将其视为与byte不同的基本组件类型的元素数组,例如int[]或long[]。 VarHandle 的变量类型是viewArrayClass的组件类型,坐标类型列表是(ByteBuffer, int),其中int坐标类型对应于一个参数,该参数是byte[]数组的索引。返回的 VarHandle 访问ByteBuffer中的索引处的字节,根据给定的字节顺序将字节组成到或从viewArrayClass的组件类型的值。支持的组件类型(变量类型)是
short、char、int、long、float和double。如果
ByteBuffer是只读的,则访问将导致ReadOnlyBufferException除了读取访问模式之外的任何内容。如果索引小于
0或大于ByteBuffer限制减去T的大小(以字节为单位),则访问给定索引处的字节将导致IndexOutOfBoundsException。T在索引处访问字节可能对齐或未对齐,相对于底层内存地址,比如A,与ByteBuffer和索引相关联。如果访问未对齐,则对get和set访问模式以外的任何访问都将导致IllegalStateException。在这种情况下,原子访问只能保证A的 GCD 和T的大小(以字节为单位)的最大幂次方。如果访问对齐,则支持以下访问模式并保证支持原子访问:- 所有
T的读写访问模式,32位平台上long和double的访问模式get和set除外。 int、long、float或double的原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他类型。)int和long的数字原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)int和long的按位原子更新访问模式。 (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)
可以为
ByteBuffer、bb(直接或其他方式)、index、T及其相应的盒装类型T_BOX确定未对齐的访问,因此原子性保证,如下所示:int sizeOfT = T_BOX.BYTES; // size in bytes of T ByteBuffer bb = ... int misalignedAtIndex = bb.alignmentOffset(index, sizeOfT); boolean isMisaligned = misalignedAtIndex != 0;如果变量类型是
float或double,则原子更新访问模式使用它们的按位表示比较值(分别参见Float.floatToRawIntBits(float)和Double.doubleToRawLongBits(double))。- 参数:
viewArrayClass- 视图数组类,组件类型为TbyteOrder- 存储在底层ByteBuffer中的视图数组元素的字节顺序(请注意,这会覆盖ByteBuffer的字节顺序)- 返回:
-
一个 VarHandle,可以访问
ByteBuffer的元素,被视为与视图数组类的组件类型相对应的元素 - 抛出:
NullPointerException- 如果 viewArrayClass 或 byteOrder 为空IllegalArgumentException- 如果 viewArrayClass 不是数组类型UnsupportedOperationException- 如果不支持将 viewArrayClass 的组件类型作为变量类型- 自从:
- 9
- 所有
-
spreadInvoker
生成一个方法句柄,它将调用给定type的任何方法句柄,给定数量的尾随参数替换为单个尾随Object[]数组。生成的调用程序将是一个带有以下参数的方法句柄:- 单个
MethodHandle目标 - 零个或多个前导值(由
leadingArgCount计算) - 包含尾随参数的
Object[]数组
调用程序将调用其目标,就像调用带有指示的
type的invoke一样。也就是说,如果目标恰好是给定的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为空IllegalArgumentException- 如果leadingArgCount不在从 0 到type.parameterCount()的范围内,或者如果生成的方法句柄的类型为 太多的参数
- 单个
-
exactInvoker
生成一个特殊的 invoker method handle 可用于调用给定类型的任何方法句柄,就像通过invokeExact一样。生成的调用程序将具有与所需类型完全相同的类型,除了它将接受类型为MethodHandle的附加前导参数。此方法等效于以下代码(尽管它可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)Discussion: 调用程序方法句柄在使用未知类型的变量方法句柄时很有用。例如,要模拟对变量方法句柄
M的invokeExact调用,提取其类型T,为T查找调用方法X,并调用调用方法,如X.invoke(T, A...)。 (调用X.invokeExact是行不通的,因为类型T是未知的。)如果需要传播、收集或其他参数转换,它们可以应用于调用程序X并在许多M方法句柄值上重复使用,只要它们与X的类型兼容。(Note: The invoker method is not available via the Core Reflection API. An attempt to call java.lang.reflect.Method.invoke on the declared
invokeExactorinvokemethod will raise anUnsupportedOperationException.)此方法不会抛出反射或安全异常。
- 参数:
type- 所需的目标类型- 返回:
- 适合调用给定类型的任何方法句柄的方法句柄
- 抛出:
IllegalArgumentException- 如果生成的方法句柄的类型为 太多的参数
-
invoker
产生一个特殊的 invoker method handle 可用于调用与给定类型兼容的任何方法句柄,就像通过invoke一样。生成的调用程序将具有与所需类型完全相同的类型,除了它将接受类型为MethodHandle的附加前导参数。在调用其目标之前,如果目标与预期类型不同,调用程序将根据需要应用引用强制转换,并对基本值进行装箱、拆箱或加宽,就像
asType一样。同样,返回值将根据需要进行转换。如果目标是 变量 arity 方法句柄 ,则将进行所需的元数转换,就像asType一样。此方法等效于以下代码(尽管它可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invoke", type)Discussion: A 一般方法类型 是仅提及
Object参数和返回值的一个。这种类型的调用者能够调用与通用类型具有相同数量的任何方法句柄。(Note: The invoker method is not available via the Core Reflection API. An attempt to call java.lang.reflect.Method.invoke on the declared
invokeExactorinvokemethod will raise anUnsupportedOperationException.)此方法不会抛出反射或安全异常。
- 参数:
type- 所需的目标类型- 返回:
- 适用于调用可转换为给定类型的任何方法句柄的方法句柄
- 抛出:
IllegalArgumentException- 如果生成的方法句柄的类型为 太多的参数
-
varHandleExactInvoker
生成一个特殊的 invoker method handle,它可用于在关联访问模式类型与给定类型兼容的任何 VarHandle 上调用签名多态访问模式方法。生成的调用程序将具有与所需的给定类型完全相同的类型,除了它将接受类型为VarHandle的附加前导参数。- 参数:
accessMode- VarHandle 访问模式type- 所需的目标类型- 返回:
- 适用于调用访问模式类型为给定类型的任何 VarHandle 的访问模式方法的方法句柄。
- 自从:
- 9
-
varHandleInvoker
生成一个特殊的 invoker method handle,它可用于在关联访问模式类型与给定类型兼容的任何 VarHandle 上调用签名多态访问模式方法。生成的调用程序将具有与所需的给定类型完全相同的类型,除了它将接受类型为VarHandle的附加前导参数。在调用其目标之前,如果访问模式类型与所需的给定类型不同,则调用程序将根据需要应用引用强制转换,并对基本值进行装箱、拆箱或加宽,就像
asType一样。同样,返回值将根据需要进行转换。此方法等效于以下代码(尽管它可能更有效):
publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)- 参数:
accessMode- VarHandle 访问模式type- 所需的目标类型- 返回:
- 适用于调用任何 VarHandle 的访问模式方法的方法句柄,其访问模式类型可转换为给定类型。
- 自从:
- 9
-
explicitCastArguments
生成一个方法句柄,它通过成对参数和返回类型转换使给定方法句柄的类型适应新类型。原始类型和新类型必须具有相同数量的参数。生成的方法句柄保证报告一个等于所需新类型的类型。如果原始类型和新类型相等,则返回目标。
允许与
MethodHandle.asType相同的转换,如果这些转换失败,也会应用一些额外的转换。给定类型 T0 , T1 ,如果可能,在asType完成的任何转换之前或代替应用以下转换之一:- 如果 T0 和 T1 是引用,而 T1 是接口类型,则类型 T0 的值将作为 T1 传递,无需转换。 (这种接口处理遵循字节码验证器的用法。)
- 如果 T0 是boolean而 T1 是另一个原语,则boolean将转换为字节值,1 表示真,0 表示假。 (此处理遵循字节码验证器的用法。)
- 如果 T1 是 boolean 且 T0 是另一个原语,则 T0 通过 Java 强制转换 (JLS 5.5 ) 转换为字节,并测试结果的低位,就像
(x & 1) != 0一样。 - 如果 T0 和 T1 是除boolean以外的原语,则应用 Java 强制转换 (JLS 5.5)。 (具体来说,T0 将通过加宽和/或变窄转换为 T1。)
- 如果 T0 是引用而 T1 是基元,则将在运行时应用拆箱转换,可能随后对基元值进行 Java 强制转换(JLS 5.5 ),随后可能通过测试低阶从字节到boolean的转换少量。
- 如果 T0 是引用而 T1 是原语,并且如果引用在运行时为空,则引入零值。
- 参数:
target- 重新键入参数后要调用的方法句柄newType- 新方法句柄的预期类型- 返回:
- 一个方法句柄,在执行任何必要的参数转换后委托给目标,并安排任何必要的返回值转换
- 抛出:
NullPointerException- 如果任一参数为空WrongMethodTypeException- 如果无法进行转换- 参见:
-
permuteArguments
public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) 通过重新排序参数,生成一个方法句柄,该方法句柄将给定方法句柄的调用序列调整为新类型。生成的方法句柄保证报告一个等于所需新类型的类型。给定的数组控制重新排序。调用
#I传入参数个数(值为newType.parameterCount(),调用#O传出参数个数(值为target.type().parameterCount())。则重排数组的长度必须为#O,且每个元素必须为小于#I的非负数. 对于每一个小于#O的N,第N个传出参数将从第I个传入参数中获取,其中I是reorder[N]。不应用参数或返回值转换。由
newType确定的每个传入参数的类型必须与目标方法句柄中相应传出参数或参数的类型相同。newType的返回类型必须与原始目标的返回类型相同。重新排序的数组不需要指定实际的排列。如果传入参数的索引在数组中出现多次,则传入参数将被复制;如果传入参数的索引未出现在数组中,则传入参数将被删除。与
dropArguments的情况一样,重新排序数组中未提及的传入参数可以是任何类型,仅由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: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 重新排序参数后要调用的方法句柄newType- 新方法句柄的预期类型reorder- 控制重新排序的索引数组- 返回:
- 在删除未使用的参数并移动和/或复制其他参数后委托给目标的方法句柄
- 抛出:
NullPointerException- 如果任何参数为空IllegalArgumentException- 如果索引数组长度不等于目标的元数,或者如果任何索引数组元素不是newType参数的有效索引,或者如果target.type()和newType中的两个相应参数类型不相同,
-
constant
生成请求的返回类型的方法句柄,每次调用时返回给定的常量值。在返回方法句柄之前,将传入的值转换为请求的类型。如果请求的类型是原始类型,则尝试扩大原始类型转换,否则尝试引用转换。
返回的方法句柄相当于
identity(type).bindTo(value)。- 参数:
type- 所需方法句柄的返回类型value- 要返回的值- 返回:
- 给定返回类型的方法句柄,没有参数,它总是返回给定值
- 抛出:
NullPointerException- 如果type参数为空ClassCastException- 如果无法将值转换为所需的返回类型IllegalArgumentException- 如果给定类型是void.class
-
identity
生成一个方法句柄,该句柄在调用时返回其唯一参数。- 参数:
type- 唯一参数的类型和所需方法句柄的返回值- 返回:
- 接受并返回给定类型的一元方法句柄
- 抛出:
NullPointerException- 如果参数为空IllegalArgumentException- 如果给定类型是void.class
-
zero
生成所请求返回类型的常量方法句柄,每次调用时返回该类型的默认值。生成的常量方法句柄将没有副作用。返回的方法句柄相当于
empty(methodType(type))。它也等同于explicitCastArguments(constant(Object.class, null), methodType(type)),因为explicitCastArguments将null转换为默认值。- 参数:
type- 所需方法句柄的预期返回类型- 返回:
- 一个常量方法句柄,它不带参数并返回给定类型的默认值(或 void,如果类型为 void)
- 抛出:
NullPointerException- 如果参数为空- 自从:
- 9
- 参见:
-
empty
生成请求类型的方法句柄,该句柄忽略任何参数,不执行任何操作,并根据返回类型返回合适的默认值。也就是说,它返回一个零原始值,一个null或void。返回的方法句柄相当于
dropArguments(zero(type.returnType()), 0, type.parameterList())。- API 注意:
-
给定谓词和目标,可以生成有用的“if-then”结构,如
guardWithTest(pred, target, empty(target.type())。 - 参数:
type- 所需方法句柄的类型- 返回:
- 给定类型的常量方法句柄,它返回给定返回类型的默认值
- 抛出:
NullPointerException- 如果参数为空- 自从:
- 9
- 参见:
-
insertArguments
在方法句柄调用之前为目标方法句柄提供一个或多个bound arguments。对应于绑定参数的目标的形式参数称为 bound parameters 。返回一个保存绑定参数的新方法句柄。当它被调用时,它接收任何非绑定参数的参数,将保存的参数绑定到它们的相应参数,并调用原始目标。新方法句柄的类型将从原始目标类型中删除绑定参数的类型,因为新方法句柄将不再需要其调用者提供这些参数。
每个给定的参数对象必须匹配相应的绑定参数类型。如果绑定参数类型是原始类型,则参数对象必须是包装器,并且将被拆箱以生成原始值。
pos参数选择要绑定的参数。它的范围可能介于零和N-L(包括),其中N是目标方法句柄的元数,并且L是值数组的长度。Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 插入参数后要调用的方法句柄pos- 插入参数的位置(第一个为零)values- 要插入的参数系列- 返回:
- 在调用原始方法句柄之前插入附加参数的方法句柄
- 抛出:
NullPointerException- 如果目标或values数组为空IllegalArgumentException- 如果 (@code pos) 小于0或大于N - L,其中N是目标方法句柄的元数,L是值数组的长度。ClassCastException- 如果参数与相应的绑定参数类型不匹配。- 参见:
-
dropArguments
生成一个方法句柄,该句柄将在调用其他指定的参数之前丢弃一些伪参数目标方法句柄。新方法句柄的类型将与目标的类型相同,除了它还将在某个给定位置包括虚拟参数类型。pos参数的范围可能介于零和N, 在哪里N是目标的元数。如果pos为零,则虚拟参数将在目标的实际参数之前;如果pos是N他们会来的。示例:
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- 如果目标为空,或者如果valueTypes列表或其任何元素为空IllegalArgumentException- 如果valueTypes的任何元素是void.class,或者如果pos是负数或大于目标的元数,或者如果新方法句柄的类型有太多参数
-
dropArguments
生成一个方法句柄,该句柄将在调用其他指定的参数之前丢弃一些伪参数目标方法句柄。新方法句柄的类型将与目标的类型相同,除了它还将在某个给定位置包括虚拟参数类型。pos参数的范围可能介于零和N, 在哪里N是目标的元数。如果pos为零,则虚拟参数将在目标的实际参数之前;如果pos是N他们会来的。- 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) 调整目标方法句柄以匹配给定的参数类型列表。如有必要,添加伪参数。在匹配开始之前可以跳过一些前导参数。target的参数类型列表中的其余类型必须是起始位置pos的newTypes类型列表的子列表。生成的句柄将具有目标句柄的参数类型列表,任何不匹配的参数类型(在匹配子列表之前或之后)插入目标原始参数的相应位置,就像dropArguments(MethodHandle, int, Class[])一样。生成的句柄将具有与目标句柄相同的返回类型。
用更正式的术语来说,假设这两个类型列表:
- 目标句柄具有参数类型列表
S..., M...,S中的类型与skip指示的一样多。M类型应该与给定类型列表newTypes的一部分相匹配。 newTypes列表包含类型P..., M..., A...,P中的类型与pos指示的一样多。M类型正是目标句柄的参数类型列表中的M类型应该匹配的类型。A中的类型是在匹配子列表之后找到的附加类型。
dropArgumentsToMatch的结果将具有参数类型列表S..., P..., M..., A...,其中插入了P和A类型,就像由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位置的参数类型。- 自从:
- 9
- 目标句柄具有参数类型列表
-
dropReturn
删除目标句柄的返回值(如果有)。返回的方法句柄将具有void返回类型。- 参数:
target- 适应的方法句柄- 返回:
- 一个可能适应的方法句柄
- 抛出:
NullPointerException- 如果target为空- 自从:
- 16
-
filterArguments
通过预处理一个或多个参数来调整目标方法句柄,每个参数都有自己的一元过滤函数,然后调用目标,每个预处理参数替换为相应过滤函数的结果。预处理由一个或多个方法句柄执行,在
filters数组的元素中指定。过滤器数组的第一个元素对应于目标的pos参数,依此类推。过滤器函数按从左到右的顺序调用。数组中的空参数被视为恒等函数,相应的参数保持不变。 (如果数组中没有非空元素,则返回原始目标。)每个过滤器都应用于适配器的相应参数。
如果过滤器
F适用于目标的Nth 参数,则F必须是一个只接受一个参数的方法句柄。F的唯一参数的类型替换了生成的适配方法句柄中目标的相应参数类型。F的返回类型必须与目标的相应参数类型相同。如果
filters的元素(无论是否为 null)与目标中的参数位置不对应,则会出错。示例:
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/p和B/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...); }Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 过滤参数后要调用的方法句柄pos- 要过滤的第一个参数的位置filters- 方法句柄最初调用过滤参数- 返回:
- 包含指定参数过滤逻辑的方法句柄
- 抛出:
NullPointerException- 如果目标为空或filters数组为空IllegalArgumentException- 如果filters的非空元素与上述目标的相应参数类型不匹配,或者如果pos+filters.length大于target.type().parameterCount(),或者如果生成的方法句柄的类型为 太多的参数
-
collectArguments
通过使用过滤器(另一个方法句柄)预处理其参数的子序列来调整目标方法句柄。预处理的参数被过滤器函数的结果(如果有的话)替换。然后在修改后的(通常是缩短的)参数列表上调用目标。如果过滤器返回一个值,则目标必须接受该值作为其位于
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的签名和参数中找到,除非V是void。A/a和C/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...); } // and if the filter has no arguments: T target2(A...,V,C...); V filter2(); T adapter2(A... a,C... c) { V v = filter2(); return target2(a...,v,c...); } // and if the filter has a void return: 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()); //step 2 mh = MethodHandles.foldArguments(mh, coll); //step 1coll的结果(如果有)之外不使用任何参数,则collectArguments(mh, 0, coll)等同于filterReturnValue(coll, mh)。如果过滤器方法句柄coll使用一个参数并产生非空结果,则collectArguments(mh, N, coll)等同于filterArguments(mh, N, coll)。其他等价是可能的,但需要参数排列。Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 过滤参数子序列后要调用的方法句柄pos- 传递给过滤器的第一个适配器参数的位置,和/或接收过滤器结果的目标参数filter- 调用参数子序列的方法句柄- 返回:
- 包含指定参数子序列过滤逻辑的方法句柄
- 抛出:
NullPointerException- 如果任一参数为空IllegalArgumentException- 如果filter的返回类型是非空的并且与目标的pos参数不同,或者如果pos不在 0 和目标的元数之间(含),或者如果生成的方法句柄的类型为 太多的参数- 参见:
-
filterReturnValue
通过使用过滤器(另一个方法句柄)后处理其返回值(如果有)来调整目标方法句柄。过滤器的结果从适配器返回。如果目标返回一个值,则过滤器必须接受该值作为其唯一参数。如果目标返回 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); } // and if the target has a void return: void target2(A...); V filter2(); V adapter2(A... a) { target2(a...); return filter2(); } // and if the filter has a void return: T target3(A...); void filter3(V); void adapter3(A... a) { T t = target3(a...); filter3(t); }Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 在过滤返回值之前调用的方法句柄filter- 调用返回值的方法句柄- 返回:
- 包含指定返回值过滤逻辑的方法句柄
- 抛出:
NullPointerException- 如果任一参数为空IllegalArgumentException- 如果filter的参数列表与上述目标的返回类型不匹配
-
foldArguments
通过预处理它的一些参数来调整目标方法句柄,然后使用预处理的结果调用目标,将其插入到原始参数序列中。预处理由第二个方法句柄
combiner执行。在传递给适配器的参数中,第一个N个参数被复制到组合器,然后调用组合器。 (这里,N被定义为组合器的参数计数。)在此之后,控制传递给目标,组合器的任何结果都插入到原始N传入参数之前。如果组合器返回一个值,则目标的第一个参数类型必须与组合器的返回类型相同,并且目标的下一个
N参数类型必须与组合器的参数完全匹配。如果组合器返回 void,则不会插入任何结果,并且目标的第一个
N参数类型必须与组合器的参数完全匹配。生成的适配器与目标的类型相同,除了第一个参数类型被删除,如果它对应于组合器的结果。
(请注意,
dropArguments可用于删除组合器或目标不希望接收的任何参数。如果某些传入参数仅用于组合器,请考虑使用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); // also prints "boo": assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));这是生成的适配器的伪代码。在代码中,
T表示target和结果适配器的结果类型。V/v表示折叠位置之前的target的形参和自变量的类型和值;V也是combiner的结果类型。A/a表示折叠位置的N参数和参数的类型和值。B/b表示折叠参数和参数后面的target参数和参数的类型和值。// there are N arguments in A... T target(V, A[N]..., B...); V combiner(A...); T adapter(A... a, B... b) { V v = combiner(a...); return target(v, a..., b...); } // and if the combiner has a void return: T target2(A[N]..., B...); void combiner2(A...); T adapter2(A... a, B... b) { combiner2(a...); return target2(a..., b...); }Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 组合参数后要调用的方法句柄combiner- 最初调用传入参数的方法句柄- 返回:
- 包含指定参数折叠逻辑的方法句柄
- 抛出:
NullPointerException- 如果任一参数为空IllegalArgumentException- 如果combiner的返回类型为非空且与目标的第一个参数类型不同,或者如果目标的初始N参数类型(跳过与combiner的返回类型匹配的)与combiner的参数类型
-
foldArguments
通过预处理它的一些参数来调整目标方法句柄,从给定位置开始,然后使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。此方法与
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); // also prints "jum": assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));这是生成的适配器的伪代码。在代码中,
T表示target和结果适配器的结果类型。V/v表示折叠位置之前的target的形参和自变量的类型和值;V也是combiner的结果类型。A/a表示折叠位置的N参数和参数的类型和值。Z/z和B/b分别表示以pos开始的折叠参数和参数之前和之后的target参数和参数的类型和值。// there are N arguments in A... 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...); } // and if the combiner has a void return: T target2(Z..., A[N]..., B...); void combiner2(A...); T adapter2(Z... z, A... a, B... b) { combiner2(a...); return target2(z..., a..., b...); }Note: 结果适配器永远不是 变量方法句柄 ,即使原始目标方法句柄是。
- 参数:
target- 组合参数后要调用的方法句柄pos- 开始折叠和插入折叠结果的位置;如果这是0,效果与foldArguments(MethodHandle, MethodHandle)相同。combiner- 最初调用传入参数的方法句柄- 返回:
- 包含指定参数折叠逻辑的方法句柄
- 抛出:
NullPointerException- 如果任一参数为空IllegalArgumentException- 如果满足以下两个条件之一: (1)combiner的返回类型是非void并且与目标签名的位置pos处的参数类型不同; (2) 目标签名位置pos处的N参数类型(跳过与combiner的返回类型匹配的一个)与combiner的参数类型不同。- 自从:
- 9
- 参见:
-
guardWithTest
public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) 通过用测试保护它来创建一个适应目标方法句柄的方法句柄,一个boolean方法句柄。如果守卫失败,则会调用回退句柄。所有三个方法句柄必须具有相同的对应参数和返回类型,除了测试的返回类型必须是boolean,并且测试允许比其他两个方法句柄具有更少的参数。这是生成的适配器的伪代码。代码中
T表示涉及的三个句柄的结果类型统一;A/a,target参数的类型和值以及test消耗的参数;和B/b,target参数和参数的那些类型和值不被test使用。请注意,测试参数(伪代码中的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- 用于测试的方法句柄,必须返回booleantarget- 测试通过时调用的方法句柄fallback- 测试失败时调用的方法句柄- 返回:
- 包含指定的 if/then/else 逻辑的方法句柄
- 抛出:
NullPointerException- 如果任何参数为空IllegalArgumentException- 如果test不返回boolean,或者所有三种方法类型都不匹配(test的返回类型更改为与目标的返回类型匹配)。
-
catchException
public static MethodHandle catchException(MethodHandle target, Class <? extends Throwable > exType, MethodHandle handler) 通过在异常处理程序中运行它来创建一个适应目标方法句柄的方法句柄。如果目标正常返回,则适配器返回该值。如果抛出与指定类型匹配的异常,则调用回退句柄而不是异常,加上原始参数。目标和处理程序必须具有相同的相应参数和返回类型,但处理程序可以省略尾随参数(类似于
guardWithTest中的谓词)。此外,处理程序必须有一个额外的前导参数exType或超类型。这是生成的适配器的伪代码。在代码中,
T代表target和handler的返回类型,对应的是结果适配器的返回类型;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- 如果任何参数为空IllegalArgumentException- 如果handler不接受给定的异常类型,或者如果方法句柄类型在它们的返回类型和它们相应的参数中不匹配- 参见:
-
throwException
public static MethodHandle throwException(Class <?> returnType, Class <? extends Throwable > exType) 生成一个方法句柄,它将抛出给定exType的异常。方法句柄将接受exType的单个参数,并立即将其作为异常抛出。方法类型将名义上指定返回returnType。返回类型可以是任何方便的类型:这对方法句柄的行为无关紧要,因为它永远不会正常返回。- 参数:
returnType- 所需方法句柄的返回类型exType- 所需方法句柄的参数类型- 返回:
- 可以抛出给定异常的方法句柄
- 抛出:
NullPointerException- 如果任一参数为空
-
loop
构造一个表示循环的方法句柄,其中包含多个循环变量,这些循环变量在每次迭代时更新和检查。由于其中一个谓词而导致循环终止时,将运行相应的终结器并传递循环的结果,即结果句柄的返回值。直观地说,每个循环都由一个或多个“子句”组成,每个子句指定一个本地 iteration variable 和/或一个循环出口。循环的每次迭代按顺序执行每个子句。一个子句可以选择更新它的迭代变量;它还可以选择执行测试和条件循环退出。为了用方法句柄来表达这个逻辑,每个子句将指定最多四个独立的动作:
- init: 在循环执行之前,初始化一个
V类型的迭代变量v。 - step: 当子句执行时,迭代变量
v的更新步骤。 - pred: 当一个子句执行时,一个谓词执行以测试 for 循环退出。
- fini: 如果子句导致循环退出,则终结器执行以计算循环的返回值。
(V...)。值本身将是(v...)。当我们谈到“参数列表”时,我们通常指的是类型,但在某些上下文中(描述执行)列表将是实际值。根据某些规则,这些子句部分中的一些可能会被省略,并且在这种情况下提供了有用的默认行为。有关详细说明,请参见下文。
Parameters optional everywhere: 允许但不要求每个子句函数接受每个迭代变量的参数
v。作为例外,init 函数不能采用任何v参数,因为在执行 init 函数时尚未计算这些值。任何子句函数都可能忽略采用它有权采用的参数的任何尾随子序列。事实上,任何子句函数都可能根本不带任何参数。Loop parameters: 子句函数可以采用它有权使用的所有迭代变量值,在这种情况下,它还可以采用更多尾随参数。此类额外值称为 loop parameters ,其类型和值标记为
(A...)和(a...)。这些成为生成的循环句柄的参数,在执行循环时提供。 (由于初始化函数不接受迭代变量v,初始化函数的任何参数自动成为循环参数a。)与迭代变量一样,允许但不要求子句函数接受循环参数。这些循环参数充当在整个循环中可见的循环不变值。Parameters visible everywhere: 每个非 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*)。Checking clause structure: 给定一组子句,执行许多检查和调整以连接循环的所有部分。它们在下面的步骤中有详细说明。在这些步骤中,“必须”一词的每次出现都对应于一个地方,如果循环组合器的输入不满足所需的约束条件,将抛出
IllegalArgumentException。Effectively identical sequences: 参数列表
A被定义为另一个参数列表Beffectively identical 如果A和B相同,或者如果A更短并且与适当的前缀B相同。当谈到一组无序的参数列表时,如果该集合包含最长列表,并且该集合的所有成员都与该最长列表有效相同,则我们说该集合作为一个整体“有效相同”。例如,(V*)形式的任何一组类型序列实际上是相同的,如果添加更多(V... A*)形式的序列,情况也是如此。Step 0: Determine clause structure.
- 子句数组(
MethodHandle[][]类型)必须是非null且至少包含一个元素。 - 子句数组不能包含
null或长度超过四个元素的子数组。 - 短于四个元素的子句被视为好像它们被
null个元素填充到长度为四。通过将元素附加到数组来进行填充。 - 包含所有
null的子句将被忽略。 - 每个子句都被视为一个四元组函数,称为“init”、“step”、“pred”和“fini”。
Step 1A: Determine iteration variable types
(V...).- 每个子句的迭代变量类型是使用子句的 init 和 step 返回类型确定的。
- 如果两个函数都被省略,则对应的子句没有迭代变量(
void用作指示该类型的类型)。如果省略其中一个,则另一个的返回类型定义子句的迭代变量类型。如果两者都给出,则公共返回类型(它们必须相同)定义子句的迭代变量类型。 - 形成返回类型列表(按子句顺序),省略所有出现的
void。 - 此类型列表称为“迭代变量类型”(
(V...))。
Step 1B: Determine loop parameters
(A...).- 检查并收集 init 函数参数列表(其形式为
(A*))。 - 在移除迭代变量类型后,检查并收集 step、pred 和 fini 参数列表的后缀。 (它们必须具有
(V... A*)的形式;仅收集(A*)部分。) - 不要从不以所有迭代变量类型开头的 step、pred 和 fini 参数列表中收集后缀。 (将在步骤 2 中检查这些类型以及所有子句函数类型。)
- 省略的子句函数将被忽略。 (等效地,它们被视为具有空参数列表。)
- 所有收集的参数列表必须有效地相同。
- 最长的参数列表(必须是唯一的)称为“外部参数列表”(
(A...))。 - 如果没有这样的参数列表,则将外部参数列表视为空序列。
- 由迭代变量类型和后跟外部参数类型组成的组合列表称为“内部参数列表”。
Step 1C: Determine loop return type.
- 检查 fini 函数返回类型,忽略省略的 fini 函数。
- 如果没有 fini 函数,则循环返回类型为
void。 - 否则,fini 函数的公共返回类型
R(它们的返回类型必须相同)定义循环返回类型。
Step 1D: Check other types.
- 必须至少有一个非省略的 pred 函数。
- 每个未省略的 pred 函数都必须有一个
boolean返回类型。
Step 2: Determine parameter lists.
- 生成的循环句柄的参数列表将是外部参数列表
(A...)。 - 初始化函数的参数列表将调整为外部参数列表。 (请注意,它们的参数列表实际上已经与该列表相同。)
- 每个非省略、非初始化(step、pred 和 fini)函数的参数列表必须与内部参数列表
(V... A...)有效地相同。
Step 3: Fill in omitted functions.
- 如果省略了 init 函数,则使用 默认值 作为子句的迭代变量类型。
- 如果省略了阶跃函数,则使用子句迭代变量类型的恒等函数;在前面子句的非
void迭代变量的身份函数参数之前插入丢弃的参数参数。 (这会将循环变量变成局部循环不变量。) - 如果省略 pred 函数,则使用常量
true函数。 (就此子句而言,这将使循环继续进行。请注意,在这种情况下,无法访问相应的 fini 函数。) - 如果省略 fini 函数,则使用 默认值 作为循环返回类型。
Step 4: Fill in missing parameter types.
- 此时,每个 init 函数参数列表实际上与外部参数列表
(A...)相同,但有些列表可能更短。对于每个带有短参数列表的 init 函数,填充列表的末尾。 - 此时,每个非 init 函数参数列表实际上与内部参数列表
(V... A...)相同,但有些列表可能更短。对于每个具有短参数列表的非初始化函数,填充列表的末尾。 - 参数列表由 删除未使用的尾随参数 填充。
Final observations.
- 在这些步骤之后,所有子句都通过提供省略的函数和参数进行了调整。
- 所有 init 函数都有一个公共参数类型列表
(A...),最终循环句柄也将具有该列表。 - 所有 fini 函数都有一个共同的返回类型
R,最终循环句柄也将有。 - 所有非初始化函数都有一个公共参数类型列表
(V... A...),其中包含(非void)迭代变量V后跟循环参数。 - 每对 init 和 step 函数的返回类型都一致
V。 - 每个非初始化函数将能够观察所有迭代变量的当前值
(v...)。 - 每个函数都将能够观察所有循环参数的传入值
(a...)。
Example. 作为上述步骤 1A 的结果,
loop组合器具有以下属性:- 给定
N子句Cn = {null, Sn, Pn}和n = 1..N。 - 假设谓词句柄
Pn是null或没有参数。 (只有一个Pn必须是非null。) - 假设步骤句柄
Sn具有签名(B1..BX)Rn,对于某些常量X>=N。 - 假设
Q是非空类型Rn的计数,而(V1...VQ)是这些类型的序列。 n = 1..min(X,Q)必须是Vn == Bn。- 参数类型
Vn将被解释为循环局部状态元素(V...)。 - 任何剩余类型
BQ+1..BX(如果是Q<X)将确定生成的循环句柄的参数类型(A...)。
(A...)派生自步骤函数,如果大部分循环计算发生在步骤中,这是很自然的。对于某些循环,计算负担可能在 pred 函数中最重,因此 pred 函数可能需要接受循环参数值。对于具有复杂退出逻辑的循环,fini 函数可能需要接受循环参数,同样对于具有复杂入口逻辑的循环,init 函数将需要额外的参数。出于这些原因,确定这些参数的规则在所有子句部分中尽可能对称。通常,循环参数在整个循环中充当公共不变值,而迭代变量充当公共变量值,或者(如果没有阶梯函数)作为内部循环不变量临时值。Loop execution.
- 调用循环时,循环输入值保存在局部变量中,以传递给每个子句函数。这些局部变量是循环不变的。
- 每个 init 函数都按子句顺序执行(传递外部参数
(a...))并将非void值保存(作为迭代变量(v...))到局部变量中。这些局部变量将循环变化(除非它们的步骤表现为恒等函数,如上所述)。 - 所有函数执行(初始化函数除外)都将传递内部参数列表,包括非
void迭代值(v...)(按子句顺序)然后循环输入(a...)(按参数顺序)。 - 然后按子句顺序(pred 之前的步骤)执行 step 和 pred 函数,直到 pred 函数返回
false。 - 步进函数调用的非
void结果用于更新循环变量序列(v...)中的相应值。更新后的值对所有后续函数调用立即可见。 - 如果 pred 函数返回
false,则调用相应的 fini 函数,并将结果值(类型R)作为一个整体从循环中返回。 - 如果所有 pred 函数始终返回 true,则不会调用任何 fini 函数,并且除非抛出异常,否则循环无法退出。
Usage tips.
- 虽然每个步进函数都会接收all循环变量的当前值,但有时一个步进函数只需要观察其自身变量的当前值。在这种情况下,阶跃函数可能需要显式 删除所有前面的循环变量 。这将需要在类似
dropArguments(step, 0, V0.class, ...)的表达式中提及它们的类型。 - 循环变量不需要改变;它们可以是循环不变的。子句可以通过合适的 init 函数创建循环不变性,而没有 step、pred 或 fini 函数。这可能有助于将传入的循环参数“连接”到相邻循环变量的 step 或 pred 函数中。
- 如果某些子句函数是实例上的虚方法,则可以使用诸如
new MethodHandle[]{identity(ObjType.class)}之类的初始子句将实例本身方便地放置在初始不变循环“变量”中。在那种情况下,实例引用将是第一个迭代变量值,并且将虚方法用作子句部分将很容易,因为它们都将采用与该值匹配的前导实例引用。
这是生成的循环句柄的伪代码。如上,
V和v代表循环变量的类型和取值;A和a表示传递给整个循环的参数;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 注意:
-
示例:
同样的示例,删除参数并使用组合器:
// iterative implementation of the factorial function as a loop handle 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; } // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods // null initializer for counter, should initialize to 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));一个类似的示例,使用一个辅助对象来保存一个循环参数:// simplified implementation of the factorial function as a loop handle static int inc(int i) { return i + 1; } // drop acc, k static int mult(int i, int acc) { return i * acc; } //drop k static boolean cmp(int i, int k) { return i < k; } // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods // null initializer for counter, should initialize to 0 MethodHandle MH_one = MethodHandles.constant(int.class, 1); MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop 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));// instance-based implementation of the factorial function as a loop handle 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; } } // assume MH_FacLoop is a handle to the constructor // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods // null initializer for counter, should initialize to 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
- 参见:
- init: 在循环执行之前,初始化一个
-
whileLoop
从初始值设定项、主体和谓词构造一个while循环。这是 通用循环组合器 的便利包装器。pred句柄描述了循环条件;和body,它的主体。此方法产生的循环将在每次迭代中首先评估谓词,然后执行其主体(如果谓词的计算结果为true)。一旦谓词的计算结果为false,循环就会终止(在这种情况下不会执行主体)。init句柄描述了一个附加的可选循环局部变量的初始值。在每次迭代中,此循环局部变量(如果存在)将传递给body并使用从其调用返回的值进行更新。循环执行的结果将是附加循环局部变量(如果存在)的最终值。以下规则适用于这些参数句柄:
body句柄不能是null;它的类型必须是(V A...)V形式,其中V是非void,否则是(A...)void。 (在void的情况下,我们将类型void分配给名称V,我们将编写(V A...)V并理解void类型V从参数列表中悄悄删除,留下(A...)V。)- 主体的参数列表
(V A...)称为 internal parameter list 。它将约束其他循环部分的参数列表。 - 如果从内部参数列表中删除迭代变量类型
V,则生成的较短列表(A...)称为 external parameter list。 - 主体返回类型
V,如果非void,则确定循环的附加状态变量的类型。正文必须接受并返回这种类型的值V。 - 如果
init是非null,它必须有返回类型V。它的参数列表(一些 表格(A*))必须是 有效相同 到外部参数列表(A...)。 - 如果
init是null,循环变量将被初始化为其 默认值 。 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 注意:
-
示例:
, 该方法的实现可以表示如下:
// implement the zip function for lists as a loop handle 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; } // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods 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- 如果pred或body是null。- 自从:
- 9
- 参见:
-
doWhileLoop
从初始值设定项、主体和谓词构造一个do-while循环。这是 通用循环组合器 的便利包装器。pred句柄描述了循环条件;和body,它的主体。此方法产生的循环将在每次迭代中首先执行其主体,然后评估谓词。一旦执行主体后谓词的计算结果为false,循环将终止。init句柄描述了一个附加的可选循环局部变量的初始值。在每次迭代中,此循环局部变量(如果存在)将传递给body并使用从其调用返回的值进行更新。循环执行的结果将是附加循环局部变量(如果存在)的最终值。以下规则适用于这些参数句柄:
body句柄不能是null;它的类型必须是(V A...)V形式,其中V是非void,否则是(A...)void。 (在void的情况下,我们将类型void分配给名称V,我们将编写(V A...)V并理解void类型V从参数列表中悄悄删除,留下(A...)V。)- 主体的参数列表
(V A...)称为 internal parameter list 。它将约束其他循环部分的参数列表。 - 如果从内部参数列表中删除迭代变量类型
V,则生成的较短列表(A...)称为 external parameter list。 - 主体返回类型
V,如果非void,则确定循环的附加状态变量的类型。正文必须接受并返回这种类型的值V。 - 如果
init是非null,它必须有返回类型V。它的参数列表(一些 表格(A*))必须是 有效相同 到外部参数列表(A...)。 - 如果
init是null,循环变量将被初始化为其 默认值 。 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; } // assume MH_zero, MH_step, and MH_pred are handles to the above methods 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- 如果pred或body是null。- 自从:
- 9
- 参见:
-
countedLoop
public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) 构造一个运行给定迭代次数的循环。这是 通用循环组合器 的便利包装器。迭代次数由
iterations句柄评估结果决定。循环计数器i是类型为int的额外循环迭代变量。它将被初始化为 0 并在每次迭代中递增 1。如果
body句柄返回非void类型V,则还存在该类型的前导循环迭代变量。此变量使用可选的init句柄初始化,或者如果句柄为null则初始化为V类型的 默认值。在每次迭代中,迭代变量都传递给
body句柄的调用。从正文(类型为V)返回的非void值更新前导迭代变量。循环句柄执行的结果将是该变量的最终V值(如果没有V变量,则为void)。以下规则适用于参数句柄:
iterations句柄不能是null,并且必须返回类型int,此处在参数类型列表中称为I。body句柄不能是null;它的类型必须是(V I A...)V形式,其中V是非void,否则是(I A...)void。 (在void的情况下,我们将类型void分配给名称V,我们将编写(V I A...)V并理解void类型V从参数列表中悄悄删除,留下(I A...)V。)- 主体的参数列表
(V I A...)有助于称为 internal parameter list 的类型列表。它将约束其他循环部分的参数列表。 - 作为一种特殊情况,如果主体仅提供
V和I类型,而没有额外的A类型,则内部参数列表由iterations句柄的参数类型A...扩展。 - 如果从内部参数列表中删除迭代变量类型
(V I),则生成的较短列表(A...)称为 external parameter list。 - 主体返回类型
V,如果非void,则确定循环的附加状态变量的类型。主体必须既接受前导参数又返回此类型的值V。 - 如果
init是非null,它必须有返回类型V。它的参数列表(一些 form(A*))必须是 有效相同 到外部参数列表(A...)。 - 如果
init是null,循环变量将被初始化为其 默认值 。 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; // => a variation on a well known theme static String step(String v, int counter, String init) { return "na " + v; } // assume MH_step is a handle to the method above 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; // => a variation on a well known theme static String step(String v, int counter ) { return "na " + v; } // assume MH_step is a handle to the method above 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; // => a variation on a well known theme static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } // assume MH_step is a handle to the method above 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; // => a variation on a well known theme static String step(String v, int counter, String pre) { return pre + " " + v; } // assume MH_step is a handle to the method above 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。它控制标准情况下的循环参数和结果类型(详见上文)。它必须接受自己的返回类型(如果非空)加上一个int参数(用于计数器),并且可以接受任意数量的附加类型。有关其他限制,请参见上文。- 返回:
- 表示循环的方法句柄。
- 抛出:
NullPointerException- 如果iterations或body句柄是null。IllegalArgumentException- 如果任何参数违反了上面制定的规则。- 自从:
- 9
- 参见:
-
countedLoop
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) 构造一个对一定范围内的数字进行计数的循环。这是 通用循环组合器 的便利包装器。循环计数器
i是类型为int的循环迭代变量。start和end句柄确定循环计数器的开始(包括)和结束(不包括)值。循环计数器将初始化为从start句柄的计算返回的int值,并运行到从end返回的值(独占),步长为1。如果
body句柄返回非void类型V,则还存在该类型的前导循环迭代变量。此变量使用可选的init句柄初始化,或者如果句柄为null则初始化为V类型的 默认值。在每次迭代中,迭代变量都传递给
body句柄的调用。从正文(类型为V)返回的非void值更新前导迭代变量。循环句柄执行的结果将是该变量的最终V值(如果没有V变量,则为void)。以下规则适用于参数句柄:
start和end句柄不能是null,并且都必须返回通用类型int,此处在参数类型列表中称为I。body句柄不能是null;它的类型必须是(V I A...)V形式,其中V是非void,否则是(I A...)void。 (在void的情况下,我们将类型void分配给名称V,我们将编写(V I A...)V并理解void类型V从参数列表中悄悄删除,留下(I A...)V。)- 主体的参数列表
(V I A...)有助于称为 internal parameter list 的类型列表。它将约束其他循环部分的参数列表。 - 作为一种特殊情况,如果主体仅提供
V和I类型,而没有额外的A类型,则内部参数列表由end句柄的参数类型A...扩展。 - 如果从内部参数列表中删除迭代变量类型
(V I),则生成的较短列表(A...)称为 external parameter list。 - 主体返回类型
V,如果非void,则确定循环的附加状态变量的类型。主体必须既接受前导参数又返回此类型的值V。 - 如果
init是非null,它必须有返回类型V。它的参数列表(一些 form(A*))必须是 有效相同 到外部参数列表(A...)。 - 如果
init是null,循环变量将被初始化为其 默认值 。 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); // assume MH_increment and MH_predicate are handles to implementation-internal methods with // the following semantics: // 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) { // ignore the V variable incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i) pred = dropArguments(pred, 1, returnType); // ditto retv = dropArguments(identity(returnType), 0, counterType); // ignore limit } body = dropArguments(body, 0, counterType); // ignore the limit variable 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。它控制标准情况下的循环参数和结果类型(详见上文)。它必须接受自己的返回类型(如果非空)加上一个int参数(用于计数器),并且可以接受任意数量的附加类型。有关其他限制,请参见上文。- 返回:
- 表示循环的方法句柄。
- 抛出:
NullPointerException- 如果start、end或body句柄中的任何一个是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句柄的调用。从正文(类型为V)返回的非void值更新前导迭代变量。循环句柄执行的结果将是该变量的最终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...)有助于称为 internal parameter list 的类型列表。它将约束其他循环部分的参数列表。 - 作为一种特殊情况,如果主体仅提供
V和T类型,没有额外的A类型,则内部参数列表由iterator句柄的参数类型A...扩展;如果它是null,则添加单一类型Iterable并构成A...列表。 - 如果从内部参数列表中删除迭代变量类型
(V T),则生成的较短列表(A...)称为 external parameter list。 - 主体返回类型
V,如果非void,则确定循环的附加状态变量的类型。主体必须既接受前导参数又返回此类型的值V。 - 如果
init是非null,它必须有返回类型V。它的参数列表(一些 form(A*))必须是 有效相同 到外部参数列表(A...)。 - 如果
init是null,循环变量将被初始化为其 默认值 。 - 如果
iterator句柄是非null,则它必须具有返回类型java.util.Iterator或其子类型。它在执行循环时产生的迭代器将假定产生可以转换为类型T的值。 - 非
null(某种形式(A*))的iterator的参数列表必须与外部参数列表(A...)有效相同。 - 如果
iterator是null它默认为一个行为类似于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...); // defaults to 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 注意:
-
示例:
,该方法的实现可以大致表示如下:
// get an iterator from a list static List<String> reverseStep(List<String> r, String e) { r.add(0, e); return r; } static List<String> newArrayList() { return new ArrayList<>(); } // assume MH_reverseStep and MH_newArrayList are handles to the above methods 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) { // assume MH_next, MH_hasNext, MH_startIter are handles to methods of 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) { // the simple thing first: in (I V A...), drop the I to get V retv = dropArguments(identity(returnType), 0, Iterator.class); // body type signature (V T A...), internal loop types (I V A...) step = swapArguments(body, 0, 1); // swap 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。它控制标准情况下的循环参数和结果类型(详见上文)。它必须接受自己的返回类型(如果非空)加上一个T参数(用于迭代值),并且可以接受任意数量的附加类型。有关其他限制,请参见上文。- 返回:
- 体现迭代循环功能的方法句柄。
- 抛出:
NullPointerException- 如果body句柄是null。IllegalArgumentException- 如果任何参数违反上述要求。- 自从:
- 9
-
tryFinally
通过将target方法句柄包装在try-finally块中来创建一个方法句柄。另一个方法句柄cleanup表示finally块的功能。target句柄执行期间抛出的任何异常都将传递给cleanup句柄。异常将被重新抛出,除非cleanuphandle 先抛出异常。cleanup句柄的执行返回的值将是try-finally句柄的执行结果。cleanup句柄将传递一个或两个额外的前导参数。第一个是target句柄执行期间抛出的异常,如果没有抛出异常则为null。第二个是target句柄的执行结果,或者,如果它抛出异常,则提供所需类型的null、零或false值作为占位符。如果target句柄具有void返回类型,则第二个参数不存在。 (请注意,除了参数类型转换之外,组合器通过省略相应的矛盾参数而不是通过插入null或零值来表示参数列表中的void值。)target和cleanup句柄必须具有相同的对应参数和返回类型,但cleanup句柄可以省略尾随参数。此外,cleanup句柄必须有一个或两个额外的前导参数:- 一个
Throwable,它将携带target句柄抛出的异常(如果有);和 - 与
target和cleanup的返回类型相同类型的参数,它将携带执行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 = (zero value for 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抛出什么异常。如果target抛出的异常类型不可分配给cleanup的第一个参数类型,则声明较窄的类型可能会导致try-finally句柄抛出ClassCastException。请注意,原则上几乎任何类型的 Java 代码都可以抛出VirtualMachineError、LinkageError和RuntimeException的各种异常类型,并且仅捕获(比方说)IOException的 finally 子句将掩盖ClassCastException后面的任何其他异常类型。- 参数:
target- 其执行将被包装在try块中的句柄。cleanup- 在 finally 块中调用的句柄。- 返回:
-
包含由两个参数组成的
try-finally块的方法句柄。 - 抛出:
NullPointerException- 如果任何参数为空IllegalArgumentException- 如果cleanup不接受所需的前导参数,或者方法句柄类型在它们的返回类型和相应的尾随参数中不匹配- 自从:
- 9
- 参见:
- 一个
-
tableSwitch
创建一个表切换方法句柄,可用于根据给定的目标索引(称为选择器)切换一组目标方法句柄。对于
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- 如果fallback、targets数组或targets数组的任何元素是null。IllegalArgumentException- 如果targets数组为空,如果回退句柄或任何目标句柄的前导参数不是int,或者如果回退句柄和所有目标句柄的类型不同。
-
memorySegmentViewVarHandle
memorySegmentViewVarHandle是 Java 平台的预览 API。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。创建一个 var 句柄对象,可用于取消引用 内存段PREVIEW 通过将其内容视为提供的值布局的序列。提供的布局指定载体类型PREVIEW , 字节大小PREVIEW , 字节对齐PREVIEW 和字节顺序PREVIEW 与返回的 var 句柄关联。
返回的 var 句柄的类型是
carrier,坐标类型列表是(MemorySegment, long),其中long坐标类型对应于给定内存段中的字节偏移量。返回的 var 句柄访问给定内存段中偏移量处的字节,根据给定的字节顺序将字节组合到或从类型为carrier的值;生成的 var 句柄的对齐约束(以字节为单位)由alignmentBytes给出。例如,考虑由
GroupLayout表示的内存布局PREVIEW 实例构造如下:要访问名为GroupLayout seq = java.lang.foreign.MemoryLayout.structLayout( MemoryLayout.paddingLayout(32), 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 注意:
-
生成的 var 句柄具有某些特征访问模式限制,这是所有内存段视图 var 句柄所共有的。内存段视图 var 句柄与访问大小
S和对齐约束B相关联(均以字节表示)。如果内存访问操作发生在与对齐约束S和B兼容的内存地址A处,我们称该内存访问操作为 fully aligned。如果访问完全对齐,则支持以下访问模式并保证支持原子访问:- 所有
T的读写访问模式,32位平台上long和double的访问模式get和set除外。 int、long、float、double或MemorySegment的原子更新访问模式PREVIEW (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他类型。)int、long和MemorySegment的数字原子更新访问模式PREVIEW (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)int、long和MemorySegment的按位原子更新访问模式PREVIEW (JDK 的未来主要平台版本可能支持某些当前不支持的访问模式的其他数字类型。)
T是float、double或MemorySegmentPREVIEW 然后原子更新访问模式使用它们的按位表示比较值(参见Float.floatToRawIntBits(float)、Double.doubleToRawLongBits(double)和MemorySegment.address()PREVIEW , 分别)。或者,内存访问操作是 partially aligned 如果它发生在内存地址
A仅与对齐约束B兼容;在这种情况下,访问get和set访问模式以外的任何内容都将导致IllegalStateException。如果访问是部分对齐的,则原子访问只保证关于A和S的 GCD 的最大幂。在所有其他情况下,我们称内存访问操作为 misaligned ;在这种情况下,无论使用何种访问模式,都会抛出
IllegalStateException。最后,如果
T是MemorySegment所有写访问模式都会抛出IllegalArgumentException除非要写入的值是 本国的PREVIEW 内存段。 - 所有
- 参数:
layout- 要为其获取内存访问句柄的值布局。- 返回:
- 新的内存段视图 var 句柄。
- 抛出:
IllegalArgumentException- 如果使用了非法载体类型,或者如果alignmentBytes不是 2 的幂。NullPointerException- 如果layout是null。- 自从:
- 19
- 参见:
-
filterValue
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) filterValue是 Java 平台的预览 API。程序只能在启用预览功能时使用filterValue。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。过滤器函数预处理传入和传出值来调整目标 var 句柄。当在生成的 var 句柄上调用 eg
VarHandle.set(Object...)时,传入值(类型为T,其中T是第一个过滤器函数的 last 参数类型)使用第一个过滤器进行处理,然后传递给目标 var 句柄。相反,当在生成的 var 句柄上调用 egVarHandle.get(Object...)时,从目标 var 句柄(类型T,其中T是第二个过滤器函数的 last 参数类型)获得的返回值使用第二个过滤器处理并返回给调用者.更高级的访问模式类型,例如VarHandle.AccessMode.COMPARE_AND_EXCHANGE可能会同时应用这两个过滤器。为了使装箱和拆箱过滤器格式正确,它们的类型必须分别为
(A... , S) -> T和(A... , T) -> S形式,其中T是目标变量句柄的类型。如果是这种情况,生成的 var 句柄将具有类型S并将具有附加坐标A...(将附加到目标 var 句柄的坐标)。如果装箱和拆箱过滤器在调用时抛出任何已检查的异常,则生成的 var 句柄将抛出
IllegalStateException。生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 目标变量句柄filterToTarget- 将某些类型S转换为target类型的过滤器filterFromTarget- 将target类型转换为某种类型S的过滤器- 返回:
- 接受新类型的适配器变量句柄,执行提供的装箱/拆箱转换。
- 抛出:
IllegalArgumentException- 如果filterFromTarget和filterToTarget格式不正确,也就是说,它们分别具有(A... , S) -> T和(A... , T) -> S以外的类型,其中T是目标 var 句柄的类型,或者如果确定filterFromTarget或filterToTarget抛出任何已检查的异常.NullPointerException- 如果任何参数是null。- 自从:
- 19
-
filterCoordinates
filterCoordinates是 Java 平台的预览 API。程序只能在启用预览功能时使用filterCoordinates。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。通过使用一元过滤函数预处理传入的坐标值来调整目标变量句柄。当在生成的 var 句柄上调用 eg
VarHandle.get(Object...)时,从位置pos开始的传入坐标值(类型C1, C2 ... Cn,其中C1, C2 ... Cn是一元过滤函数的返回类型)被转换为新值(类型S1, S2 ... Sn,其中S1, S2 ... Sn是参数一元过滤器函数的类型),然后传递(连同适配未更改的任何坐标)到目标 var 句柄。对于格式正确的坐标过滤器,它们的类型必须是
S1 -> T1, S2 -> T1 ... Sn -> Tn形式,其中T1, T2 ... Tn是从目标变量句柄的位置pos开始的坐标类型。如果任何过滤器在调用时抛出检查异常,则生成的 var 句柄将抛出
IllegalStateException。生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 目标变量句柄pos- 要变换的第一个坐标的位置filters- 用于转换从位置pos开始的坐标的一元函数- 返回:
- 接受新坐标类型的适配器变量句柄,将提供的转换应用于新坐标值。
- 抛出:
IllegalArgumentException- 如果filters中的句柄格式不正确,即它们具有S1 -> T1, S2 -> T2, ... Sn -> Tn以外的类型,其中T1, T2 ... Tn是从目标变量句柄的位置pos开始的坐标类型,如果pos不在 0 和目标变量句柄坐标之间arity,inclusive,或者如果提供的过滤器比从pos开始的可用坐标类型的实际数量更多,或者如果确定任何过滤器抛出任何已检查的异常。NullPointerException- 如果任何参数是null或filters包含null。- 自从:
- 19
-
insertCoordinates
insertCoordinates是 Java 平台的预览 API。程序只能在启用预览功能时使用insertCoordinates。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。在调用 var 句柄之前,为目标 var 句柄提供一个或多个 bound coordinates。因此,生成的 var 句柄将具有比目标 var 句柄更少的坐标类型。当在生成的 var 句柄上调用 eg
VarHandle.get(Object...)时,传入的坐标值与绑定坐标值连接,然后传递给目标 var 句柄。为了使绑定坐标格式正确,它们的类型必须是
T1, T2 ... Tn,其中T1, T2 ... Tn是从目标 var 句柄的位置pos开始的坐标类型。生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 插入绑定坐标后要调用的 var 句柄pos- 要插入的第一个坐标的位置values- 要插入的一系列绑定坐标- 返回:
- 在调用目标变量句柄之前插入附加坐标的适配器变量句柄
- 抛出:
IllegalArgumentException- 如果pos不在 0 和目标 var 句柄坐标 arity 之间,包括在内,或者如果提供的值多于从pos开始的可用坐标类型的实际数量。ClassCastException- 如果values中的绑定坐标格式不正确,即它们的类型不是T1, T2 ... Tn,其中T1, T2 ... Tn是从目标变量句柄的位置pos开始的坐标类型。NullPointerException- 如果任何参数是null或values包含null。- 自从:
- 19
-
permuteCoordinates
public static VarHandle permuteCoordinates(VarHandle target, List <Class <?>> newCoordinates, int... reorder) permuteCoordinates是 Java 平台的预览 API。程序只能在启用预览功能时使用permuteCoordinates。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。提供一个 var 句柄,它通过重新排列目标 var 句柄的坐标值来调整它们,以便新坐标与提供的坐标相匹配。给定的数组控制重新排序。调用
#I传入坐标数(值newCoordinates.size()),并调用#O传出坐标数(与目标 var 句柄关联的坐标数)。那么重新排序数组的长度必须是#O,并且每个元素必须是小于#I的非负数。对于每一个小于#O的N,第N个输出坐标将从第I个输入坐标中获取,其中I是reorder[N]。不应用坐标值转换。
newCoordinates确定的每个传入坐标的类型必须与目标 var 句柄中相应传出坐标的类型相同。重新排序的数组不需要指定实际的排列。如果传入坐标的索引在数组中出现多次,则传入坐标将被复制;如果传入坐标的索引未出现在数组中,则传入坐标将被丢弃。
生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 坐标重新排序后要调用的 var 句柄newCoordinates- 新坐标类型reorder- 控制重新排序的索引数组- 返回:
- 在调用目标变量句柄之前重新排列传入坐标值的适配器变量句柄
- 抛出:
IllegalArgumentException- 如果索引数组长度不等于目标 var 句柄的坐标数,或者如果任何索引数组元素不是newCoordinates坐标的有效索引,或者如果目标 var 句柄中的两个对应坐标类型和在newCoordinates中不相同。NullPointerException- 如果任何参数是null或newCoordinates包含null。- 自从:
- 19
-
collectCoordinates
collectCoordinates是 Java 平台的预览 API。程序只能在启用预览功能时使用collectCoordinates。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。通过使用过滤器(方法句柄)预处理其坐标值的子序列来调整目标变量句柄。预处理的坐标被过滤函数的结果(如果有的话)替换,然后在修改后的(通常是缩短的)坐标列表上调用目标变量句柄。如果
R是过滤器的返回类型(不能为空),则目标 var 句柄必须接受类型为R的值作为其在位置pos的坐标,之前和/或之后是任何未传递给过滤器的坐标。没有坐标被重新排序,并且从过滤器返回的结果替换(按顺序)最初传递给适配器的整个坐标子序列。过滤器的参数类型(如果有的话)在生成的适配变量句柄中替换位置
pos处的目标变量句柄的零个或一个坐标类型。过滤器的返回类型必须与位置pos处的目标变量句柄的坐标类型相同,并且该目标变量句柄坐标由过滤器的返回值提供。如果任何过滤器在调用时抛出检查异常,则生成的 var 句柄将抛出
IllegalStateException。生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 过滤坐标后要调用的 var 句柄pos- 待过滤坐标的位置filter- 过滤器方法句柄- 返回:
- 在调用目标变量句柄之前过滤传入坐标值的适配器变量句柄
- 抛出:
IllegalArgumentException- 如果filter的返回类型为 void,或者它与目标 var 句柄的pos坐标不同,如果pos不在 0 和目标 var 句柄坐标之间,包括在内,如果生成的 var 句柄的类型将有 坐标太多 ,或者如果确定filter抛出任何已检查的异常。NullPointerException- 如果任何参数是null。- 自从:
- 19
-
dropCoordinates
dropCoordinates是 Java 平台的预览 API。程序只能在启用预览功能时使用dropCoordinates。预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。返回一个 var 句柄,它将在委托给目标 var 句柄之前丢弃一些虚拟坐标。因此,生成的 var 句柄将具有比目标 var 句柄更多的坐标类型。pos参数的范围可能介于零和N, 在哪里N是目标变量句柄坐标类型的元数。如果pos为零,虚拟坐标将在目标的实际参数之前;如果pos是N他们会来的。生成的 var 句柄将具有与目标 var 句柄相同的访问模式(请参阅
VarHandle.AccessMode)和原子访问保证。- 参数:
target- 删除虚拟坐标后要调用的 var 句柄pos- 要放置的第一个坐标的位置(最左边为零)valueTypes- 要放置的坐标类型- 返回:
- 一个适配器变量句柄,它在调用目标变量句柄之前删除一些虚拟坐标
- 抛出:
IllegalArgumentException- 如果pos不在 0 和目标 var 句柄坐标 arity 之间,包括在内。NullPointerException- 如果任何参数是null或valueTypes包含null。- 自从:
- 19
-
memorySegmentViewVarHandle。