- java.lang.Object
-
- java.lang.invoke.MethodHandles
-
public class MethodHandles extends Object
此类仅包含对方法句柄进行操作或返回方法句柄的静态方法。 它们分为几类:- 查找方法有助于为方法和字段创建方法句柄。
- 组合方法,将预先存在的方法句柄组合或转换为新方法。
- 创建方法句柄的其他工厂方法,用于模拟其他常见JVM操作或控制流模式。
- 从以下版本开始:
- 1.7
-
-
嵌套类汇总
嵌套类 变量和类型 类 描述 static class
MethodHandles.Lookup
查找对象是用于在创建需要访问检查时创建方法句柄的工厂。
-
方法摘要
所有方法 静态方法 具体的方法 变量和类型 方法 描述 static MethodHandle
arrayConstructor(类<?> arrayClass)
生成一个方法句柄,构造所需类型的数组,就好像通过anewarray
字节码一样。static MethodHandle
arrayElementGetter(类<?> arrayClass)
生成一个方法句柄,提供对数组元素的读访问,就像aaload
字节码一样。static MethodHandle
arrayElementSetter(类<?> arrayClass)
生成一个方法句柄,提供对数组元素的写访问,就像astore
字节码一样。static VarHandle
arrayElementVarHandle(类<?> arrayClass)
产生VarHandle给予访问类型的数组的元素arrayClass
。static MethodHandle
arrayLength(类<?> arrayClass)
生成一个返回数组长度的方法句柄,就像arraylength
字节码一样。static VarHandle
byteArrayViewVarHandle(类<?> viewArrayClass, ByteOrder byteOrder)
生成一个VarHandle,可以访问byte[]
数组的元素,就像它是一个不同的原始数组类型一样,例如int[]
或long[]
。static VarHandle
byteBufferViewVarHandle(类<?> viewArrayClass, ByteOrder byteOrder)
生成一个VarHandle,可以访问ByteBuffer的元素,就像它是ByteBuffer
的不同原始组件类型的元素数组byte
,例如int[]
或long[]
。static MethodHandle
catchException(MethodHandle target, 类<? extends Throwable> exType, MethodHandle handler)
通过在异常处理程序中运行,使方法句柄适应目标方法句柄。static MethodHandle
collectArguments(MethodHandle target, int pos, MethodHandle filter)
通过使用过滤器(另一个方法句柄)预处理其参数的子序列来调整目标方法句柄。static MethodHandle
constant(类<?> type, Object value)
生成请求的返回类型的方法句柄,每次调用它时都返回给定的常量值。static MethodHandle
countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body)
构造一个运行给定迭代次数的循环。static MethodHandle
countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body)
构造一个计算一系列数字的循环。static MethodHandle
doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred)
从初始值设定项,正文和谓词构造一个do-while
循环。static MethodHandle
dropArguments(MethodHandle target, int pos, 类<?>... valueTypes)
生成一个方法句柄,它会在调用其他指定的 目标方法句柄之前丢弃一些伪参数。static MethodHandle
dropArguments(MethodHandle target, int pos, List<类<?>> valueTypes)
生成一个方法句柄,它会在调用其他指定的 目标方法句柄之前丢弃一些伪参数。static MethodHandle
dropArgumentsToMatch(MethodHandle target, int skip, List<类<?>> newTypes, int pos)
调整目标方法句柄以匹配给定的参数类型列表。static MethodHandle
empty(MethodType type)
生成所请求类型的方法句柄,该句柄忽略任何参数,不执行任何操作,并根据返回类型返回合适的默认值。static MethodHandle
exactInvoker(MethodType type)
生成一个特殊的 调用者方法句柄 ,可以用来调用给定类型的任何方法句柄,就像invokeExact
一样 。static MethodHandle
explicitCastArguments(MethodHandle target, MethodType newType)
生成一个方法句柄,通过成对参数和返回类型转换使给定方法句柄的类型适应新类型。static MethodHandle
filterArguments(MethodHandle target, int pos, MethodHandle... filters)
通过预处理一个或多个参数来调整目标方法句柄,每个参数都有自己的一元过滤函数,然后调用目标,每个预处理参数替换为其相应过滤函数的结果。static MethodHandle
filterReturnValue(MethodHandle target, MethodHandle filter)
通过使用过滤器(另一个方法句柄)对其返回值(如果有)进行后处理来调整目标方法句柄。static MethodHandle
foldArguments(MethodHandle target, int pos, MethodHandle combiner)
通过预处理某些参数来调整目标方法句柄,从给定位置开始,然后使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。static MethodHandle
foldArguments(MethodHandle target, MethodHandle combiner)
通过预处理其某些参数来调整目标方法句柄,然后使用预处理的结果调用目标,并将其插入到原始参数序列中。static MethodHandle
guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
通过使用测试,布尔值方法句柄来保护目标方法句柄,使其适应目标方法句柄。static MethodHandle
identity(类<?> type)
生成一个方法句柄,在调用时返回其唯一的参数。static MethodHandle
insertArguments(MethodHandle target, int pos, Object... values)
在方法句柄调用之前提供带有一个或多个 绑定参数的目标方法句柄。static MethodHandle
invoker(MethodType type)
生成一个特殊的 调用者方法句柄 ,可以用来调用与给定类型兼容的任何方法句柄,就像invoke
一样 。static MethodHandle
iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body)
构造一个范围超过Iterator<T>
生成的值的Iterator<T>
。static MethodHandles.Lookup
lookup()
返回lookup object
,具有模拟调用者所有支持的字节码行为的全部功能。static MethodHandle
loop(MethodHandle[]... clauses)
构造一个表示循环的方法句柄,该循环具有多个循环变量,每次迭代时都会更新和检查这些变量。static MethodHandle
permuteArguments(MethodHandle target, MethodType newType, int... reorder)
生成一个方法句柄,通过重新排序参数,使给定方法句柄的调用序列适应新类型。static MethodHandles.Lookup
privateLookupIn(类<?> targetClass, MethodHandles.Lookup lookup)
返回lookup object
全功能模拟所有支持的字节码的行为,包括private access ,目标类。static MethodHandles.Lookup
publicLookup()
返回最低限度受信任的lookup object
。static <T extends Member>
TreflectAs(类<T> expected, MethodHandle target)
执行 direct method handle的未经检查的“破解”。static MethodHandle
spreadInvoker(MethodType type, int leadingArgCount)
生成一个方法句柄,该句柄将调用给定type
任何方法句柄,并将给定数量的尾随参数替换为单个尾随Object[]
数组。static MethodHandle
throwException(类<?> returnType, 类<? extends Throwable> exType)
生成一个方法句柄,它会抛出给定的exType
异常。static MethodHandle
tryFinally(MethodHandle target, MethodHandle cleanup)
创建一个方法句柄,通过将其包装在try-finally
块中来调整target
方法句柄。static MethodHandle
varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type)
生成一个特殊的 调用方法句柄 ,可用于在任何VarHandle上调用签名 - 多态访问模式方法,该方法的关联访问模式类型与给定类型兼容。static MethodHandle
varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type)
生成一个特殊的 调用方法句柄 ,可用于在任何VarHandle上调用签名 - 多态访问模式方法,该方法的关联访问模式类型与给定类型兼容。static MethodHandle
whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body)
从初始值设定项,正文和谓词构造一个while
循环。static MethodHandle
zero(类<?> type)
生成所请求的返回类型的常量方法句柄,该句柄在每次调用时返回该类型的默认值。
-
-
-
方法详细信息
-
lookup
public static MethodHandles.Lookup lookup()
返回lookup object
,具有模拟调用者所有支持的字节码行为的全部功能。 这些功能包括private access给呼叫者。 查找对象上的工厂方法可以为调用者通过字节码访问的任何成员创建direct method handles ,包括受保护和私有字段和方法。 该查找对象是可以被委托给可信代理的能力 。 不要将其存储在不受信任的代码可以访问它的位置。此方法对呼叫者敏感,这意味着它可以向不同的呼叫者返回不同的值。
- 结果
- 此方法的调用者的查找对象,具有私有访问权限
-
publicLookup
public static MethodHandles.Lookup publicLookup()
返回最低限度受信任的lookup object
。 查找具有PUBLIC
和UNCONDITIONAL
模式。 它只能用于为无条件导出的包中的公共类的公共成员创建方法句柄。作为纯惯例,此查找对象的lookup class将为
Object
。- API Note:
-
Object的使用是常规的,并且因为查找模式是有限的,所以不向Object,其包或其模块的内部提供特殊访问。
因此,此查找对象的查找上下文将是引导类加载器,这意味着它无法找到用户类。
讨论:该查找类可以被改变为任何其他类
C
使用形式的表达publicLookup().in(C.class)
。 但是可以通过更改类加载器来更改查找上下文。 公共查找对象始终受security manager checks的约束 。 此外,它无法访问caller sensitive methods 。 - 结果
- 最低限度信任的查找对象
-
privateLookupIn
public static MethodHandles.Lookup privateLookupIn(类<?> targetClass, MethodHandles.Lookup lookup) throws IllegalAccessException
返回lookup object
全功能模拟所有支持的字节码的行为,包括private access ,目标类。 此方法检查指定为Lookup
对象的调用方是否允许对目标类进行深度反射 。 如果m1
是包含模块lookup class
,和m2
是包含目标类的模块,则该检查可确保如果有安全管理器,则调用其
checkPermission
方法以检查ReflectPermission("suppressAccessChecks")
。- API Note:
-
模块
查找模式用于验证查找对象是由调用方模块中的代码创建的(或者是从调用方最初创建的查找对象派生的)。 具有模块
查找模式的查找对象可以与受信任方共享,而不会向PRIVATE
提供PRIVATE
和PACKAGE
访问权限。 - 参数
-
targetClass
- 目标班级 -
lookup
- 调用者查找对象 - 结果
- 具有私有访问权限的目标类的查找对象
- 异常
-
IllegalArgumentException
- 如果targetClass
是原始类型或数组类 -
NullPointerException
- 如果targetClass
或caller
是null
-
IllegalAccessException
- 如果上面指定的访问检查失败 -
SecurityException
- 如果安全经理拒绝 - 从以下版本开始:
- 9
- 另请参见:
-
MethodHandles.Lookup.dropLookupMode(int)
-
reflectAs
public static <T extends Member> T reflectAs(类<T> expected, MethodHandle target)
执行direct method handle的未经检查的“破解”。 结果就好像用户已经获得足够破坏目标方法句柄的查找对象,在目标上调用Lookup.revealDirect
来获取其符号引用,然后调用MethodHandleInfo.reflectAs
来解析对成员的符号引用。如果有安全管理器,则使用
ReflectPermission("suppressAccessChecks")
权限调用其checkPermission
方法。- 参数类型
-
T
- 所需的结果类型,Member
或子类型 - 参数
-
target
- 一个破解为符号引用组件的直接方法句柄 -
expected
- 表示所需结果类型T
的类对象 - 结果
- 对方法,构造函数或字段对象的引用
- 异常
-
SecurityException
- 如果呼叫者没有权限呼叫setAccessible
-
NullPointerException
- 如果任一参数为null
-
IllegalArgumentException
- 如果目标不是直接方法句柄 -
ClassCastException
- 如果该成员不是预期类型 - 从以下版本开始:
- 1.8
-
arrayConstructor
public static MethodHandle arrayConstructor(类<?> arrayClass) throws IllegalArgumentException
生成一个方法句柄,构造所需类型的数组,就好像通过anewarray
字节码一样。 方法句柄的返回类型是数组类型。 其唯一参数的类型将为int
,它指定数组的大小。如果使用负数组大小调用返回的方法句柄,则将抛出
NegativeArraySizeException
。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个方法句柄,可以创建给定类型的数组
- 异常
-
NullPointerException
- 如果参数是null
-
IllegalArgumentException
- 如果arrayClass
不是数组类型 - 从以下版本开始:
- 9
- 另请参见:
-
Array.newInstance(Class, int)
- See The Java™ Virtual Machine Specification:
-
6.5
anewarray
说明
-
arrayLength
public static MethodHandle arrayLength(类<?> arrayClass) throws IllegalArgumentException
生成一个返回数组长度的方法句柄,就像arraylength
字节码一样。 方法句柄的类型将具有int
作为返回类型,并且其唯一参数将是数组类型。如果使用
null
数组引用调用返回的方法句柄,则将抛出NullPointerException
。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个方法句柄,可以检索给定数组类型的数组的长度
- 异常
-
NullPointerException
- 如果参数是null
-
IllegalArgumentException
- 如果arrayClass不是数组类型 - 从以下版本开始:
- 9
- See The Java™ Virtual Machine Specification:
-
6.5
arraylength
说明
-
arrayElementGetter
public static MethodHandle arrayElementGetter(类<?> arrayClass) throws IllegalArgumentException
生成一个方法句柄,提供对数组元素的读访问,就像aaload
字节码一样。 方法句柄的类型将具有数组元素类型的返回类型。 它的第一个参数是数组类型,第二个参数是int
。调用返回的方法句柄时,将检查数组引用和数组索引。 甲
NullPointerException
将如果数组引用是被抛出null
和ArrayIndexOutOfBoundsException
如果索引是负将被抛出,或者如果它是大于或等于所述阵列的长度。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个方法句柄,可以从给定的数组类型加载值
- 异常
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果arrayClass不是数组类型 - See The Java™ Virtual Machine Specification:
-
6.5
aaload
说明
-
arrayElementSetter
public static MethodHandle arrayElementSetter(类<?> arrayClass) throws IllegalArgumentException
生成一个方法句柄,提供对数组元素的写访问,就像astore
字节码一样。 方法句柄的类型将具有void返回类型。 它的最后一个参数将是数组的元素类型。 第一个和第二个参数将是数组类型和int。调用返回的方法句柄时,将检查数组引用和数组索引。 甲
NullPointerException
将如果数组引用是被抛出null
和ArrayIndexOutOfBoundsException
如果索引是负将被抛出,或者如果它是大于或等于所述阵列的长度。- 参数
-
arrayClass
- 数组的类 - 结果
- 一个方法句柄,可以将值存储到数组类型中
- 异常
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果arrayClass不是数组类型 - See The Java™ Virtual Machine Specification:
-
6.5
aastore
说明
-
arrayElementVarHandle
public static VarHandle arrayElementVarHandle(类<?> arrayClass) throws IllegalArgumentException
产生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
,将检查数组引用和数组索引。 甲NullPointerException
将如果数组引用是被抛出null
和ArrayIndexOutOfBoundsException
如果索引是负将被抛出,或者如果它是大于或等于所述阵列的长度。- API Note:
-
由数值和原子更新访问模式执行的
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不是数组类型 - 从以下版本开始:
- 9
- 如果组件类型以外的任何其他
-
byteArrayViewVarHandle
public static VarHandle byteArrayViewVarHandle(类<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
生成一个VarHandle,可以访问byte[]
数组的元素,就像它是一个不同的原始数组类型一样,例如int[]
或long[]
。 该VarHandle的变量类型是组件类型viewArrayClass
和坐标类型的列表是(byte[], int)
,其中int
坐标类型对应于一个索引到一个参数byte[]
阵列。 返回的VarHandle访问byte[]
数组中索引处的字节,根据给定的字节顺序将字节组合到组件类型viewArrayClass
的值或从组件类型的值组成。支持的组件类型(变量类型)是
short
,char
,int
,long
,float
和double
。一个给定的索引处的字节的访问将导致
IndexOutOfBoundsException
如果索引小于0
比大于或byte[]
的阵列长度减去的大小(以字节计)T
。的指数下的字节访问可以对齐或不对齐为
T
,相对于底层的存储器地址,A
说,与所述阵列和索引相关联。 如果访问未对齐,则访问get
和set
访问模式以外的任何内容将导致IllegalStateException
。 在这种情况下,只能保证原子访问的最大功率为2,它将A
的GCD和A
的大小(以字节为单位)T
。 如果访问已对齐,则支持以下访问模式,并保证支持原子访问:- 读取所有
T
写访问模式,但访问模式get
和set
(long
和double
在32位平台上double
。 - 原子更新的接入方式为
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
- 视图数组类,其类型为T
-
byteOrder
- 视图数组元素的字节顺序,存储在底层byte
数组中 - 结果
-
一个VarHandle,可以访问
byte[]
数组的元素,byte[]
查看对应于视图数组类的组件类型的元素一样 - 异常
-
NullPointerException
- 如果viewArrayClass或byteOrder为null -
IllegalArgumentException
- 如果viewArrayClass不是数组类型 -
UnsupportedOperationException
- 如果不支持viewArrayClass的组件类型作为变量类型 - 从以下版本开始:
- 9
- 读取所有
-
byteBufferViewVarHandle
public static VarHandle byteBufferViewVarHandle(类<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
生成一个VarHandle,可以访问ByteBuffer的元素,就像它是ByteBuffer
的不同原始组件类型的元素数组byte
,例如int[]
或long[]
。 该VarHandle的变量类型是组件类型viewArrayClass
和坐标类型的列表是(ByteBuffer, int)
,其中int
坐标类型对应于一个索引到一个参数byte[]
阵列。 返回VarHandle的指数下访问以字节ByteBuffer
,构成字节或从的部件类型的值viewArrayClass
根据给定的字节顺序。支持的组件类型(变量类型)是
short
,char
,int
,long
,float
和double
。如果
ByteBuffer
是只读的,则访问将导致ReadOnlyBufferException
以外的任何读取访问模式。一个给定的索引处的字节的访问将导致
IndexOutOfBoundsException
如果索引小于0
比大于或ByteBuffer
的限值减去的大小(以字节计)T
。对于
T
,索引上的字节访问可以对齐或未对齐,相对于与ByteBuffer
和索引相关联的基础内存地址A
。 如果访问未对齐,则访问get
和set
访问模式以外的任何内容将导致IllegalStateException
。 在这种情况下,原子访问仅保证相对于将A
分为A
和A
的大小(以字节为单位)的T
。 如果访问已对齐,则支持以下访问模式,并保证支持原子访问:- 读取所有
T
写访问模式,但访问模式get
和set
(long
和double
(32位平台)double
。 - 原子更新的接入方式为
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
- 视图数组类,其组件类型类型为T
-
byteOrder
- 视图数组元素的字节顺序,存储在底层ByteBuffer
(注意这将覆盖ByteBuffer
的字节顺序) - 结果
-
一个VarHandle,可以访问ByteBuffer的元素,
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
为空 -
IllegalArgumentException
- 如果leadingArgCount
不在0到type.parameterCount()
含)范围内,或者结果方法句柄的类型为 too many parameters
- 单个
-
exactInvoker
public static MethodHandle exactInvoker(MethodType type)
生成一个特殊的调用者方法句柄 ,可以用来调用给定类型的任何方法句柄,就像invokeExact
一样 。 生成的调用者将具有与所需类型完全相同的类型,除了它将接受类型为MethodHandle
的其他前导参数。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
讨论:使用未知类型的变量方法句柄时,Invoker方法句柄非常有用。 例如,要模拟对变量方法句柄
M
的invokeExact
调用,请提取其类型T
,查找调用方法X
以获取T
,并调用调用方法,如X.invoke(T, A...)
。 (调用X.invokeExact
,因为类型T
是未知的。)如果需要传播,收集或其他参数转换,它们可以应用于调用程序X
并重复使用许多M
方法句柄值,只要它们与X
的类型兼容。(注意:调用方法不能通过Core Reflection API获得。尝试在声明的
invokeExact
或invoke
方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException
。 )此方法不会引发反射或安全异常。
- 参数
-
type
- 所需的目标类型 - 结果
- 适合于调用给定类型的任何方法句柄的方法句柄
- 异常
-
IllegalArgumentException
- 如果生成的方法句柄的类型为 too many parameters
-
invoker
public static MethodHandle invoker(MethodType type)
生成一个特殊的调用者方法句柄 ,可以用来调用与给定类型兼容的任何方法句柄,就像invoke
一样 。 生成的调用者将具有与所需类型完全相同的类型,除了它将接受类型为MethodHandle
的其他前导参数。在调用目标之前,如果目标与预期类型不同,调用者将根据需要应用参考强制转换以及框,取消框或加宽原始值,就像
asType
一样 。 同样,返回值将根据需要进行转换。 如果目标是variable arity method handle ,则将再次进行所需的arity转换,就像asType
一样 。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invoke", type)
讨论: A general method type是仅提及
Object
参数和返回值的一个。 这种类型的调用者能够调用与常规类型相同的任何方法句柄。(注意:调用方法不能通过Core Reflection API获得。尝试在声明的
invokeExact
或invoke
方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException
。 )此方法不会引发反射或安全异常。
- 参数
-
type
- 所需的目标类型 - 结果
- 一个方法句柄,适用于调用可转换为给定类型的任何方法句柄
- 异常
-
IllegalArgumentException
- 如果生成的方法句柄的类型为 too many parameters
-
varHandleExactInvoker
public static MethodHandle varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type)
生成一个特殊的调用方法句柄 ,可用于在任何VarHandle上调用签名 - 多态访问模式方法,该方法的关联访问模式类型与给定类型兼容。 生成的调用程序将具有与所需给定类型完全相同的类型,除了它将接受类型为VarHandle
的其他前导参数。- 参数
-
accessMode
- VarHandle访问模式 -
type
- 所需的目标类型 - 结果
- 一个方法句柄,适用于调用其访问模式类型为给定类型的任何VarHandle的访问模式方法。
- 从以下版本开始:
- 9
-
varHandleInvoker
public static MethodHandle varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type)
生成一个特殊的调用方法句柄 ,可用于在任何VarHandle上调用签名 - 多态访问模式方法,该方法的关联访问模式类型与给定类型兼容。 生成的调用者将具有与所需给定类型完全相同的类型,除了它将接受类型为VarHandle
的其他前导参数。在调用其目标之前,如果访问模式类型与所需的给定类型不同,则调用者将根据需要应用引用强制转换以及框,取消框或加宽原始值,就像
asType
一样 。 同样,返回值将根据需要进行转换。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)
- 参数
-
accessMode
- VarHandle访问模式 -
type
- 所需的目标类型 - 结果
- 一个方法句柄,适用于调用其访问模式类型可转换为给定类型的任何VarHandle的访问模式方法。
- 从以下版本开始:
- 9
-
explicitCastArguments
public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType)
生成一个方法句柄,通过成对参数和返回类型转换使给定方法句柄的类型适应新类型。 原始类型和新类型必须具有相同数量的参数。 保证生成的方法句柄报告一个等于所需新类型的类型。如果原始类型和新类型相等,则返回target。
允许使用与
MethodHandle.asType
相同的转化,如果转化失败,也会应用一些其他转化。 给定类型T0 , T1 ,如果可能,在asType
完成任何转换之前或之后应用以下转换asType
:- 如果T0和T1是引用,并且T1是接口类型,那么类型T0的值作为T1传递而没有强制转换。 (接口的这种处理遵循字节码验证器的使用。)
- 如果T0是布尔值而T1是另一个基元,则布尔值转换为字节值,1表示true,0表示false。 (此处理遵循字节码验证程序的使用。)
- 如果T1是布尔值而T0是另一个基元,则通过Java转换(JLS 5.5)将T0转换为字节,并测试结果的低位,就好像是
(x & 1) != 0
。 - 如果T0和T1是布尔值以外的基元,则应用Java转换(JLS 5.5)。 (具体来说, T0将通过加宽和/或缩小转换为T1 。)
- 如果T0是引用而T1是原语,则将在运行时应用取消装箱转换,之后可能会对原始值进行Java转换(JLS 5.5),之后可能会通过测试低位从字节转换为布尔值位。
- 如果T0是引用而T1是基元,并且如果引用在运行时为空,则引入零值。
- 参数
-
target
- 重新输入参数后调用的方法句柄 -
newType
- 新方法句柄的预期类型 - 结果
- 一个方法句柄,在执行任何必要的参数转换后委托给目标,并安排任何必要的返回值转换
- 异常
-
NullPointerException
- 如果任一参数为null -
WrongMethodTypeException
- 如果无法进行转换 - 另请参见:
-
MethodHandle.asType(java.lang.invoke.MethodType)
-
permuteArguments
public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder)
生成一个方法句柄,通过重新排序参数,使给定方法句柄的调用序列适应新类型。 保证生成的方法句柄报告一个等于所需新类型的类型。给定的数组控制重新排序。 调用
#I
传入参数的数量(值newType.parameterCount()
,并调用#O
传出参数的数量(值target.type().parameterCount()
)。然后重新排序数组的长度必须为#O
,并且每个元素必须是小于#I
的非负数。对于每N
少于#O
N
传出参数将取自I
-th传入参数,其中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);
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 重新排序参数后调用的方法句柄 -
newType
- 新方法句柄的预期类型 -
reorder
- 控制重新排序的索引数组 - 结果
- 一个方法句柄,它在删除未使用的参数并移动和/或复制其他参数后委托给目标
- 异常
-
NullPointerException
- 如果任何参数为null -
IllegalArgumentException
- 如果索引数组长度不等于目标的arity,或者如果任何索引数组元素不是参数newType
的有效索引,或者target.type()
和newType
中的两个相应参数类型不相同,
-
constant
public static MethodHandle constant(类<?> type, Object value)
生成请求的返回类型的方法句柄,每次调用它时都返回给定的常量值。在返回方法句柄之前,传入的值将转换为请求的类型。 如果请求的类型是原始的,则尝试扩展原始转换,否则尝试引用转换。
返回的方法句柄相当于
identity(type).bindTo(value)
。- 参数
-
type
- 所需方法句柄的返回类型 -
value
- 要返回的值 - 结果
- 给定返回类型的方法句柄,没有参数,它总是返回给定的值
- 异常
-
NullPointerException
- 如果type
参数为null -
ClassCastException
- 如果值无法转换为所需的返回类型 -
IllegalArgumentException
- 如果给定的类型是void.class
-
identity
public static MethodHandle identity(类<?> type)
生成一个方法句柄,在调用时返回其唯一的参数。- 参数
-
type
- 唯一参数的类型和所需方法句柄的返回值 - 结果
- 一元方法句柄,它接受并返回给定的类型
- 异常
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果给定的类型是void.class
-
zero
public static MethodHandle zero(类<?> type)
生成所请求的返回类型的常量方法句柄,该句柄在每次调用时返回该类型的默认值。 生成的常量方法句柄没有副作用。返回的方法句柄相当于
empty(methodType(type))
。 它也相当于explicitCastArguments(constant(Object.class, null), methodType(type))
,因为explicitCastArguments
将null
转换为默认值。- 参数
-
type
- 所需方法句柄的预期返回类型 - 结果
- 一个常量方法句柄,它不接受任何参数并返回给定类型的默认值(如果类型为void,则返回void)
- 异常
-
NullPointerException
- 如果参数为null - 从以下版本开始:
- 9
- 另请参见:
-
constant(java.lang.Class<?>, java.lang.Object)
,empty(java.lang.invoke.MethodType)
,explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)
-
empty
public static MethodHandle empty(MethodType type)
生成所请求类型的方法句柄,该句柄忽略任何参数,不执行任何操作,并根据返回类型返回合适的默认值。 也就是说,它返回零原始值,null
或void
。返回的方法句柄相当于
dropArguments(zero(type.returnType()), 0, type.parameterList())
。- API Note:
-
给定谓词和目标,可以产生有用的“if-then”构造作为
guardWithTest(pred, target, empty(target.type())
。 - 参数
-
type
- 所需方法句柄的类型 - 结果
- 给定类型的常量方法句柄,它返回给定返回类型的默认值
- 异常
-
NullPointerException
- 如果参数为null - 从以下版本开始:
- 9
- 另请参见:
-
zero(java.lang.Class<?>)
,constant(java.lang.Class<?>, java.lang.Object)
-
insertArguments
public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values)
在方法句柄调用之前提供带有一个或多个绑定参数的目标方法句柄。 与绑定参数对应的目标的形式参数称为绑定参数 。 返回一个新的方法句柄,它保存绑定的参数。 调用它时,它接收任何非绑定参数的参数,将保存的参数绑定到其相应的参数,并调用原始目标。新方法句柄的类型将从原始目标类型中删除绑定参数的类型,因为新方法句柄将不再需要其调用者提供这些参数。
每个给定的参数对象必须与相应的绑定参数类型匹配。 如果绑定参数类型是基元,则参数对象必须是包装器,并且将取消装箱以生成基元值。
pos
参数选择要绑定的参数。 它可以介于0和NL之间(包含),其中N是目标方法句柄的arity, L是values数组的长度。注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 插入参数后要调用的方法句柄 -
pos
- 插入参数的位置(第一个为零) -
values
- 要插入的一系列参数 - 结果
- 在调用原始方法句柄之前插入附加参数的方法句柄
- 异常
-
NullPointerException
- 如果目标或values
数组为空 -
IllegalArgumentException
- if(@code pos)小于0
或大于N - L
,其中N
是目标方法句柄的arity,L
是values数组的长度。 -
ClassCastException
- 如果参数与相应的绑定参数类型不匹配。 - 另请参见:
-
MethodHandle.bindTo(java.lang.Object)
-
dropArguments
public static MethodHandle dropArguments(MethodHandle target, int pos, List<类<?>> valueTypes)
生成一个方法句柄,它会在调用其他指定的目标方法句柄之前丢弃一些伪参数。 新方法句柄的类型将与目标的类型相同,除了在某个给定位置还包括伪参数类型。pos
参数的范围可以介于0和N之间,其中N是目标的arity。 如果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
- 删除参数后要调用的方法句柄 -
valueTypes
- 要删除的参数的类型 -
pos
- 放下第一个参数的位置(最左边为零) - 结果
- 在调用原始方法句柄之前删除给定类型的参数的方法句柄
- 异常
-
NullPointerException
- 如果目标为null,或者valueTypes
列表或其任何元素为null -
IllegalArgumentException
-如果任何元素valueTypes
为void.class
,或者如果pos
比目标的元数负或更大,或者如果新的方法处理的类型将有太多的参数
-
dropArguments
public static MethodHandle dropArguments(MethodHandle target, int pos, 类<?>... valueTypes)
生成一个方法句柄,它会在调用其他指定的目标方法句柄之前丢弃一些伪参数。 新方法句柄的类型将与目标的类型相同,除了在某个给定位置还包括伪参数类型。pos
参数的范围可以介于0和N之间,其中N是目标的arity。 如果pos
为零,则伪参数将位于目标的实参数之前; 如果pos
是N,他们会来。- API Note:
-
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
- 删除参数后要调用的方法句柄 -
valueTypes
- 要删除的参数的类型 -
pos
- 放下第一个参数的位置(最左边为零) - 结果
- 在调用原始方法句柄之前删除给定类型的参数的方法句柄
- 异常
-
NullPointerException
- 如果目标为null,或者valueTypes
数组或其任何元素为null -
IllegalArgumentException
-如果任何元素valueTypes
为void.class
,或者如果pos
比目标的元数负或更大,或者如果新的方法处理的类型必须 too many parameters
-
dropArgumentsToMatch
public static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<类<?>> newTypes, int pos)
调整目标方法句柄以匹配给定的参数类型列表。 如有必要,添加伪参数。 在匹配开始之前可以跳过一些前导参数。target
的参数类型列表中的其余类型必须是起始位置pos
处的newTypes
类型列表的子列表。 生成的句柄将具有目标句柄的参数类型列表,其中任何非匹配的参数类型(在匹配的子列表之前或之后)插入目标的原始参数的相应位置,就像dropArguments(MethodHandle, int, Class[])
一样 。生成的句柄将具有与目标句柄相同的返回类型。
在更正式的术语中,假设这两个类型列表:
- 目标句柄具有参数类型列表
S..., M...
,在S
具有尽可能多的类型,如skip
。M
类型是那些应该与给定类型列表的一部分匹配的类型,newTypes
。 -
newTypes
列表包含类型P..., M..., A...
,在P
中具有P
所示的pos
。M
类型正是目标句柄的参数类型列表中的M
类型应该匹配的类型。A
中的类型是在匹配的子列表之后找到的其他类型。
dropArgumentsToMatch
的结果将具有参数类型列表S..., P..., M..., A...
,插入P
和A
类型,就像dropArguments(MethodHandle, int, Class[])
一样 。- API Note:
-
两个方法句柄的参数列表“实际上相同”(即,在公共前缀中相同)可以通过两次调用
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
- 如果任一参数为null -
IllegalArgumentException
-如果任何元件newTypes
是void.class
,或者如果skip
比目标的元数负或更大,或者如果pos
比newTypes列表尺寸负或更大,或者如果newTypes
不包含target
的非跳过位置pos
参数类型。 - 从以下版本开始:
- 9
- 目标句柄具有参数类型列表
-
filterArguments
public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters)
通过预处理一个或多个参数来调整目标方法句柄,每个参数都有自己的一元过滤函数,然后调用目标,每个预处理参数替换为其相应过滤函数的结果。预处理由
filters
数组的元素中指定的一个或多个方法句柄执行。 过滤器数组的第一个元素对应于目标的pos
参数,依此类推。 过滤器函数按从左到右的顺序调用。数组中的空参数被视为标识函数,相应的参数保持不变。 (如果数组中没有非null元素,则返回原始目标。)每个过滤器都应用于适配器的相应参数。
如果过滤器
F
适用于目标的N
参数,则F
必须是一个只接受一个参数的方法句柄。F
的唯一参数类型替换了生成的自适应方法句柄中目标的相应参数类型。 返回类型F
必须与目标的相应参数类型相同。如果有
filters
(null或not)的元素与目标中的参数位置不对应,则会出错。例:
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...); }
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 过滤参数后调用的方法句柄 -
pos
- 要过滤的第一个参数的位置 -
filters
- 最初在筛选参数上调用的方法句柄 - 结果
- 方法句柄,它包含指定的参数过滤逻辑
- 异常
-
NullPointerException
- 如果目标为null或filters
数组为null -
IllegalArgumentException
-如果非空元件filters
如上所述不匹配目标的对应的参数类型,或者如果pos+filters.length
大于target.type().parameterCount()
,或者如果产生的方法手柄的类型将具有 too many parameters
-
collectArguments
public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter)
通过使用过滤器(另一个方法句柄)预处理其参数的子序列来调整目标方法句柄。 预处理的参数由过滤函数的结果(如果有)替换。 然后在修改的(通常缩短的)参数列表上调用目标。如果过滤器返回一个值,则目标必须接受该值作为其位置
pos
参数,前面和/或后面跟未传递给过滤器的任何参数。 如果过滤器返回void,则目标必须接受未传递给过滤器的所有参数。 没有参数被重新排序,并且从过滤器返回的结果替换(按顺序)最初传递给适配器的参数的整个子序列。过滤器的参数类型(如果有)在生成的自适应方法句柄中的位置
pos
处替换目标的零个或一个参数类型。 过滤器的返回类型(如果有)必须与位置pos
处的目标的参数类型相同,并且该目标参数由过滤器的返回值提供。在所有情况下,
pos
必须大于或等于零,并且pos
也必须小于或等于目标的arity。例:
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
代表参数的类型和值之前和之后的收集位置,pos
,在target
的签名。 他们所得到的适配器的签名和论据,在那里他们还围绕转动起来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 1
coll
的结果(如果有)之外不使用任何参数,则collectArguments(mh, 0, coll)
等效于filterReturnValue(coll, mh)
。 如果过滤器方法句柄coll
一个参数并产生非void结果,则collectArguments(mh, N, coll)
等效于filterArguments(mh, N, coll)
。 其他等价是可能的,但需要参数置换。注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 过滤参数子序列后调用的方法句柄 -
pos
- 要传递给过滤器的第一个适配器参数的位置,和/或接收过滤器结果的目标参数 -
filter
- 调用参数子序列的方法句柄 - 结果
- 方法句柄,它包含指定的参数子序列过滤逻辑
- 异常
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果返回类型filter
是非void且与目标的pos
参数不同,或者pos
不在0和目标的arity之间(包括),或者结果方法句柄的类型将具有 too many parameters - 另请参见:
-
foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
,filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...)
,filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
-
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); } // 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); }
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 在过滤返回值之前调用的方法句柄 -
filter
- 调用返回值的方法句柄 - 结果
- 方法句柄,它包含指定的返回值过滤逻辑
- 异常
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果参数列表filter
与目标的返回类型不匹配,如上所述
-
foldArguments
public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner)
通过预处理其某些参数来调整目标方法句柄,然后使用预处理的结果调用目标,并将其插入到原始参数序列中。预处理由第二个方法句柄
combiner
执行。 在传递给适配器的参数中,第一个N
参数被复制到组合器,然后被调用。 (此处,N
被定义为组合器的参数计数。)此后,控制传递给目标,在原始N
传入参数之前插入组合器的任何结果。如果组合器返回一个值,则目标的第一个参数类型必须与组合器的返回类型相同,并且目标的下一个
N
参数类型必须与组合器的参数完全匹配。如果组合
N
返回,则不会插入任何结果,并且目标的第一个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...); }
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 组合参数后调用的方法句柄 -
combiner
- 最初在传入参数上调用的方法句柄 - 结果
- 包含指定参数折叠逻辑的方法句柄
- 异常
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果combiner
的返回类型为非void且与目标的第一个参数类型不同,或者目标的初始N
参数类型(跳过与combiner
的返回类型匹配的combiner
)与参数类型为combiner
-
foldArguments
public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner)
通过预处理某些参数来调整目标方法句柄,从给定位置开始,然后使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。此方法与
foldArguments(MethodHandle, MethodHandle)
密切相关,但允许控制参数列表中发生折叠的位置。 控制它的参数pos
是一个从零开始的索引。 上述方法foldArguments(MethodHandle, MethodHandle)
假定位置0。- API Note:
-
例:
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
表示的类型和值target
参数和自变量之前和之后的折叠参数和自变量开始pos
分别。// 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...); }
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
target
- 组合参数后调用的方法句柄 -
pos
- 开始折叠的位置以及插入折叠结果的位置; 如果这是0
,则效果与foldArguments(MethodHandle, MethodHandle)
相同。 -
combiner
- 最初在传入参数上调用的方法句柄 - 结果
- 包含指定参数折叠逻辑的方法句柄
- 异常
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
-如果满足以下两个条件成立:(1)combiner
的返回类型是非void
和不一样在位置参数类型pos
目标签名的; (2)目标签名位置pos
处的N
参数类型(跳过与combiner
的返回类型匹配的一个)与参数类型combiner
。 - 从以下版本开始:
- 9
- 另请参见:
-
foldArguments(MethodHandle, MethodHandle)
-
guardWithTest
public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
通过使用测试,布尔值方法句柄来保护目标方法句柄,使其适应目标方法句柄。 如果防护失败,则调用后备句柄。 除了测试的返回类型必须是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
- 用于测试的方法句柄,必须返回boolean -
target
- 测试通过时调用的方法句柄 -
fallback
- 如果测试失败则调用的方法句柄 - 结果
- 方法句柄,它包含指定的if / then / else逻辑
- 异常
-
NullPointerException
- 如果任何参数为null -
IllegalArgumentException
- 如果test
未返回布尔值,或者所有三种方法类型都不匹配(返回类型test
更改为与目标的匹配)。
-
catchException
public static MethodHandle catchException(MethodHandle target, 类<? 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
- 如果任何参数为null -
IllegalArgumentException
- 如果handler
不接受给定的异常类型,或者方法句柄类型的返回类型及其相应参数不匹配 - 另请参见:
-
tryFinally(MethodHandle, MethodHandle)
-
throwException
public static MethodHandle throwException(类<?> returnType, 类<? extends Throwable> exType)
生成一个方法句柄,它会抛出给定的exType
异常。 方法句柄将接受exType
的单个参数,并立即将其作为异常抛出。 方法类型名义上将指定返回returnType
。 返回类型可能是方便的:它与方法句柄的行为无关,因为它永远不会正常返回。- 参数
-
returnType
- 所需方法句柄的返回类型 -
exType
- 所需方法句柄的参数类型 - 结果
- 方法句柄,可以抛出给定的异常
- 异常
-
NullPointerException
- 如果任一参数为null
-
loop
public static MethodHandle loop(MethodHandle[]... clauses)
构造一个表示循环的方法句柄,该循环具有多个循环变量,每次迭代时都会更新和检查这些变量。 在由于其中一个谓词而终止循环时,运行相应的终结器并传递循环结果,该结果是结果句柄的返回值。直观地,每个循环由一个或多个“子句”形成,每个“子句”指定局部迭代变量和/或循环出口。 循环的每次迭代按顺序执行每个子句。 子句可以选择更新其迭代变量; 它还可以选择执行测试和条件循环退出。 为了根据方法句柄表达这个逻辑,每个子句将指定最多四个独立的动作:
- 初始化:在循环执行之前,一个迭代变量的初始化
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*)
,非初始函数参数列表的一般形式是(V*)
或(V... A*)
。检查子句结构:给定一组子句,执行了许多检查和调整以连接循环的所有部分。 它们在下面的步骤中详细说明。 在这些步骤中,如果循环组合器的输入未满足所需的约束,则“必须”一词的每次出现都对应于将抛出
IllegalArgumentException
的位置。有效相同的序列: 如果
A
和B
相同,或者如果A
较短并且与正确的前缀B
相同,则参数列表A
被定义为与另一个参数列表B
有效相同 。 当谈到一组无序的参数列表时,我们说如果集合包含最长的列表,则集合“整体上是完全相同的”,并且集合的所有成员实际上与该最长列表相同。 例如,(V*)
形式的任何一组类型序列(V*)
是相同的,并且如果添加(V... A*)
形式的更多序列,则同样如此。第0步:确定子句结构。
- 子句数组(类型
MethodHandle[][]
)必须为非null
且至少包含一个元素。 - 子句数组可能不包含
null
或超过四个元素的子数组。 - 短于四个元素的条款被视为由
null
元素填充到长度为4的元素。 通过将元素附加到数组来进行填充。 - 所有
null
的条款均被忽略。 - 每个子句被视为四元组函数,称为“init”,“step”,“pred”和“fini”。
步骤1A:确定迭代变量类型
(V...)
。- 每个子句的迭代变量类型使用子句的init和step返回类型来确定。
- 如果省略这两个函数,则没有相应子句的迭代变量(
void
用作指示该类型的类型)。 如果省略其中一个,则另一个的返回类型定义子句的迭代变量类型。 如果两者都给出,则公共返回类型(它们必须相同)定义子句的迭代变量类型。 - 形成返回类型列表(按子句顺序),省略所有出现的
void
。 - 此类型列表称为“迭代变量类型”(
(V...)
)。
步骤1B:确定循环参数
(A...)
。- 检查并收集init函数参数列表(格式为
(A*)
)。 - 删除迭代变量类型后,检查并收集step,pred和fini参数列表的后缀。 (它们的格式必须为
(V... A*)
;仅收集(A*)
部件。) - 不要从step,pred和fini参数列表中收集不以所有迭代变量类型开头的后缀。 (这些类型将在步骤2中与所有子句函数类型一起检查。)
- 省略了省略的子句函数。 (等效地,它们被视为具有空参数列表。)
- 所有收集的参数列表必须完全相同。
- 最长的参数列表(必须是唯一的)称为“外部参数列表”(
(A...)
)。 - 如果没有这样的参数列表,则外部参数列表被视为空序列。
- 由迭代变量类型和外部参数类型组成的组合列表称为“内部参数列表”。
步骤1C:确定循环返回类型。
- 检查fini函数返回类型,忽略省略的fini函数。
- 如果没有fini函数,则循环返回类型为
void
。 - 否则,fini函数的公共返回类型
R
(它们的返回类型必须相同)定义循环返回类型。
步骤1D:检查其他类型。
- 必须至少有一个未省略的pred函数。
- 每个未省略的pred函数必须具有
boolean
返回类型。
第2步:确定参数列表。
- 结果循环句柄的参数列表将是外部参数列表
(A...)
。 - init函数的参数列表将调整为外部参数列表。 (请注意,它们的参数列表已经与此列表完全相同。)
- 每个非省略的非初始(step,pred和fini)函数的参数列表必须与内部参数列表
(V... A...)
有效相同。
第3步:填写省略的功能。
- 如果省略init函数,请使用default value作为子句的迭代变量类型。
- 如果省略步进函数,则使用子句的迭代变量类型的identity function ; 在前面子句的非
void
迭代变量的identity function参数之前插入丢弃的参数参数。 (这会将循环变量转换为本地循环不变量。) - 如果省略pred函数,请使用常量
true
函数。 (就这个子句而言,这将保持循环继续。请注意,在这种情况下,相应的fini函数是不可达的。) - 如果省略fini函数,请使用default value作为循环返回类型。
第4步:填写缺少的参数类型。
- 此时,每个init函数参数列表实际上与外部参数列表
(A...)
相同,但某些列表可能更短。 对于具有短参数列表的每个init函数,填充列表的末尾。 - 此时,每个非初始函数参数列表实际上与内部参数列表
(V... A...)
相同,但某些列表可能更短。 对于具有短参数列表的每个非初始化函数,填充列表的末尾。 - 参数列表由dropping unused trailing arguments填充。
最后的观察。
- 完成这些步骤后,所有子句都通过提供省略的函数和参数进行了调整。
- 所有init函数都有一个共同的参数类型列表
(A...)
,最终的循环句柄也将具有。 - 所有fini函数都有一个共同的返回类型
R
,最终的循环句柄也将具有。 - 所有非初始函数具有公共参数类型列表
(V... A...)
,(非void
)迭代变量V
其后是循环参数。 - 每对init和step函数在它们的返回类型
V
。 - 每个非初始函数将能够观察所有迭代变量的当前值
(v...)
。 - 每个函数都能够观察到所有循环参数的输入值
(a...)
。
例。 作为上述步骤1A的结果,
loop
组合器具有以下属性:- 鉴于
N
条款Cn = {null, Sn, Pn}
和n = 1..N
。 - 假设谓词句柄
Pn
是null
或没有参数。 (只有一个Pn
必须是非null
) - 假设步骤句柄
Sn
具有签名(B1..BX)Rn
,对于某些常数X>=N
。 - 假设
Q
是非空类型Rn
的计数,而(V1...VQ)
是这些类型的序列。 - 它必须是
Vn == Bn
为n = 1..min(X,Q)
。 - 参数类型
Vn
将被解释为循环本地状态元素(V...)
。 - 任何剩余的类型
BQ+1..BX
(如果Q<X
)将确定结果循环句柄的参数类型(A...)
。
(A...)
是从步骤函数派生的,如果大多数循环计算在步骤中发生,这是很自然的。 对于某些循环,计算负担在pred函数中可能最重,因此pred函数可能需要接受循环参数值。 对于具有复杂退出逻辑的循环,fini函数可能需要接受循环参数,同样对于具有复杂条目逻辑的循环,其中init函数将需要额外的参数。 由于这些原因,确定这些参数的规则在所有子句部分中尽可能对称。 通常,循环参数在整个循环中用作公共不变值,而迭代变量用作公共变量值,或者(如果没有步进函数)用作内部循环不变的临时值。循环执行。
- 调用循环时,循环输入值将保存在本地,以传递给每个子句函数。 这些本地人是循环不变的。
- 每个init函数按子句顺序执行(传递外部参数
(a...)
),非void
值保存(作为迭代变量(v...)
)到本地。 这些局部变量将是循环变化的(除非它们的步骤表现为身份函数,如上所述)。 - 所有函数执行(init函数除外)都将传递内部参数列表,包括非
void
迭代值(v...)
(按子句顺序),然后循环输入(a...)
(按参数顺序)。 - 然后以子句顺序(在pred之前的步骤)执行step和pred函数,直到pred函数返回
false
。 - 步进函数调用的非
void
结果用于更新循环变量序列(v...)
中的相应值。 所有后续函数调用都会立即看到更新后的值。 - 如果pred函数返回
false
,则调用相应的fini函数,并从循环中返回结果值(类型为R
)。 - 如果所有pred函数总是返回true,则不会调用任何fini函数,并且除了抛出异常外,循环不能退出。
使用提示。
- 尽管每个步骤函数都将接收所有循环变量的当前值,但有时步进函数只需要观察其自身变量的当前值。 在这种情况下,步骤函数可能需要明确drop all preceding loop variables 。 这需要在
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 Note:
-
例:
// 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
- 另请参见:
-
whileLoop(MethodHandle, MethodHandle, MethodHandle)
,doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
,countedLoop(MethodHandle, MethodHandle, MethodHandle)
,iteratedLoop(MethodHandle, MethodHandle, MethodHandle)
- 初始化:在循环执行之前,一个迭代变量的初始化
-
whileLoop
public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body)
从初始值设定项,正文和谓词构造一个while
循环。 这是generic loop combinator的便利包装。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...)
称为内部参数列表 。 它将约束其他循环部分的参数列表。 - 如果从内部参数列表中删除了迭代变量类型
V
,则生成的较短列表(A...)
称为外部参数列表 。 - 主体返回类型
V
,如果void
,则确定循环的附加状态变量的类型。 正文必须接受并返回此类型的值V
。 - 如果
init
不是null
,则它必须具有返回类型V
。 (它的一些参数列表form(A*)
)必须effectively identical的外部参数列表(A...)
。 - 如果
init
是null
,则循环变量将初始化为其default value 。 -
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 Note:
-
例:
// 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
- 另请参见:
-
loop(MethodHandle[][])
,doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
-
-
doWhileLoop
public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred)
从初始值设定项,正文和谓词构造一个do-while
循环。 这是generic loop combinator的便利包装器。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...)
称为内部参数列表 。 它将约束其他循环部分的参数列表。 - 如果从内部参数列表中删除了迭代变量类型
V
,则生成的较短列表(A...)
称为外部参数列表 。 - 主体返回类型
V
(如果非void
)确定循环的其他状态变量的类型。 正文必须接受并返回此类型的值V
。 - 如果
init
是非null
,则它必须具有返回类型V
。 其参数列表(部分form(A*)
)必须为effectively identical到外部参数列表(A...)
。 - 如果
init
是null
,则循环变量将初始化为其default value 。 -
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 Note:
-
例:
// 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
- 另请参见:
-
loop(MethodHandle[][])
,whileLoop(MethodHandle, MethodHandle, MethodHandle)
-
-
countedLoop
public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body)
构造一个运行给定迭代次数的循环。 这是generic loop combinator的便利包装。迭代次数由
iterations
句柄评估结果确定。 循环计数器i
是类型int
的额外循环迭代变量。 它将被初始化为0并在每次迭代中递增1。如果
body
句柄返回非void
类型V
,则还存在该类型的前导循环迭代变量。 这个变量是使用初始化的可选init
手柄,或到default value类型的V
如果该句柄是null
。在每次迭代中,迭代变量都会传递给
body
句柄的调用。 从正文(V
类型)返回的非void
值更新前导迭代变量。 环形手柄执行的结果将是最终V
该变量的值(或void
如果没有V
变量)。以下规则适用于参数句柄:
-
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...)
提供了一个称为内部参数列表的类型列表 。 它将约束其他循环部分的参数列表。 - 作为特殊情况,如果正文仅提供
V
和I
类型,而没有其他A
类型,则内部参数列表将通过iterations
句柄的参数类型A...
进行iterations
。 - 如果从内部参数列表中删除迭代变量类型
(V I)
,则生成的较短列表(A...)
称为外部参数列表 。 - 正文返回类型
V
(如果为非void
)确定循环的其他状态变量的类型。 正文必须都接受一个前导参数并返回此类型的值V
。 - 如果
init
是非null
,则它必须具有返回类型V
。 其参数列表(部分form(A*)
)必须为effectively identical到外部参数列表(A...)
。 - 如果
init
是null
,则循环变量将初始化为其default value 。 - 参数列表
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 Note:
-
具有完全一致的body方法的示例:
// 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
。 它控制标准情况下的循环参数和结果类型(详见上文)。 它必须接受自己的返回类型(如果是非void)加上int
参数(对于计数器),并且可以接受任意数量的其他类型。 请参阅上面的其他约束。 - 结果
- 表示循环的方法句柄。
- 异常
-
NullPointerException
- 如果iterations
或body
处理中的任何一个是null
。 -
IllegalArgumentException
- 如果有任何论点违反上述规则。 - 从以下版本开始:
- 9
- 另请参见:
-
countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle)
-
-
countedLoop
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body)
构造一个计算一系列数字的循环。 这是generic loop combinator的便利包装。循环计数器
i
是类型int
的循环迭代变量。start
和end
句柄确定循环计数器的起始值(包含)和结束(独占)值。 循环计数器将初始化为从start
句柄的int
值返回的int
值,并运行到从步长为1的end
(排他性)返回的值。如果
body
句柄返回非void
类型V
,则还存在该类型的前导循环迭代变量。 如果该句柄为null
,则使用可选的init
句柄初始化此变量,或者使用V类型的V
初始化此变量。在每次迭代中,迭代变量都会传递给
body
句柄的调用。 从正文返回的非void
值(类型为V
)更新前导迭代变量。 环形手柄执行的结果将是最终V
该变量的值(或void
如果没有V
变量)。以下规则适用于参数句柄:
-
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...)
提供了称为内部参数列表的类型列表 。 它将约束其他循环部分的参数列表。 - 作为特殊情况,如果正文仅提供
V
和I
类型,而没有其他A
类型,则内部参数列表将通过end
句柄的参数类型A...
进行end
。 - 如果从内部参数列表中删除迭代变量类型
(V I)
,则生成的较短列表(A...)
称为外部参数列表 。 - 主体返回类型
V
(如果void
)确定循环的附加状态变量的类型。 正文必须都接受一个前导参数并返回此类型的值V
。 - 如果
init
不是null
,则它必须具有返回类型V
。 其参数列表(部分form(A*)
)必须为effectively identical到外部参数列表(A...)
。 - 如果
init
是null
,则循环变量将初始化为其default value 。 - 参数列表
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 Note:
-
该方法的实现可表示如下:
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
。 它控制标准情况下的循环参数和结果类型(详见上文)。 它必须接受自己的返回类型(如果非void)加上int
参数(对于计数器),并且可以接受任意数量的其他类型。 请参阅上面的其他约束。 - 结果
- 表示循环的方法句柄。
- 异常
-
NullPointerException
-如果任何的start
,end
,或body
手柄是null
。 -
IllegalArgumentException
- 如果有任何论点违反上述规则。 - 从以下版本开始:
- 9
- 另请参见:
-
countedLoop(MethodHandle, MethodHandle, MethodHandle)
-
-
iteratedLoop
public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body)
构造一个范围超过Iterator<T>
生成的值的Iterator<T>
。 这是generic loop combinator的便利包装。迭代器本身将由
iterator
句柄的评估决定。 它产生的每个值都将存储在T
类型的循环迭代变量中。如果
body
句柄返回非void
类型V
,则还存在该类型的前导循环迭代变量。 如果该句柄为null
,则使用可选的init
句柄初始化此变量,或者使用V类型的V
初始化此变量。在每次迭代中,迭代变量都会传递给
body
句柄的调用。 从正文返回的非void
值(类型V
)更新前导迭代变量。 环形手柄执行的结果将是最终V
该变量的值(或void
如果没有V
变量)。以下规则适用于参数句柄:
-
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...)
提供了一个称为内部参数列表的类型列表 。 它将约束其他循环部分的参数列表。 - 作为特殊情况,如果正文仅贡献
V
和T
类型,而没有额外的A
类型,则内部参数列表由iterator
句柄的参数类型A...
扩展; 如果是null
,则添加单一类型Iterable
并构成A...
列表。 - 如果从内部参数列表中删除了迭代变量类型
(V T)
,则生成的较短列表(A...)
称为外部参数列表 。 - 主体返回类型
V
(如果void
)确定循环的其他状态变量的类型。 正文必须都接受一个前导参数并返回此类型的值V
。 - 如果
init
不是null
,则它必须具有返回类型V
。 其参数列表(部分form(A*)
)必须为effectively identical到外部参数列表(A...)
。 - 如果
init
是null
,则循环变量将初始化为其default value 。 - 如果
iterator
句柄为非null
,则它必须具有返回类型java.util.Iterator
或其子类型。 执行循环时它产生的迭代器将被假定为产生可转换为类型T
。 - 一个的参数列表
iterator
即非null
(的某种形式的(A*)
)必须有效地等同于外部参数列表(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 Note:
-
例:
// 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
。 它控制标准情况下的循环参数和结果类型(详见上文)。 它必须接受自己的返回类型(如果是非void)加上T
参数(对于迭代值),并且可以接受任意数量的其他类型。 请参阅上面的其他约束。 - 结果
- 一个体现迭代循环功能的方法句柄。
- 异常
-
NullPointerException
- 如果body
句柄是null
。 -
IllegalArgumentException
- 如果任何参数违反上述要求。 - 从以下版本开始:
- 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
通过省略相应的自相矛盾参数表示参数列表中的void
值,而不是通过插入null
或零值。)target
和cleanup
句柄必须具有相同的对应参数和返回类型,但cleanup
句柄可能会省略尾随参数。 此外,cleanup
句柄必须具有一个或两个额外的前导参数:- a
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
进行换tryFinally
。建议将第一个参数类型
cleanup
声明为Throwable
而不是更窄的子类型。 这确保cleanup
将始终与任何异常调用target
抛出。 声明一个较窄的类型可能会导致ClassCastException
由被抛出try-finally
手柄如果由抛出的异常的类型target
不能分配到所述第一参数类型的cleanup
。 需要注意的是各种异常类型的VirtualMachineError
,LinkageError
和RuntimeException
原则上可以通过几乎任何类型的Java代码,只有最后条款映入(说)抛出IOException
会掩盖任何其他人的后面ClassCastException
。- 参数
-
target
- 将执行包装在try
块中的try
。 -
cleanup
- finally块中调用的句柄。 - 结果
-
一个方法句柄,体现了由两个参数组成的
try-finally
块。 - 异常
-
NullPointerException
- 如果任何参数为null -
IllegalArgumentException
- 如果cleanup
不接受所需的前导参数,或者方法句柄类型的返回类型与其对应的尾随参数不匹配 - 从以下版本开始:
- 9
- 另请参见:
-
catchException(MethodHandle, Class, MethodHandle)
- a
-
-