- java.lang.Object
-
- java.lang.invoke.MethodHandles
-
public class MethodHandles extends Object
该类仅由静态方法组成,或者返回方法句柄。 它们分为几类:- 有助于创建方法和字段的方法句柄的查找方法。
- 组合方法,将预先存在的方法句柄组合或转换为新的方法。
- 其他工厂方法来创建方法句柄来模拟其他常见的JVM操作或控制流模式。
- 从以下版本开始:
- 1.7
-
-
Nested Class Summary
Nested Classes Modifier and Type Class 描述 static class
MethodHandles.Lookup
查找对象是用于创建方法句柄的工厂,当创建需要访问检查时。
-
方法摘要
所有方法 静态方法 具体的方法 Modifier and Type 方法 描述 static MethodHandle
arrayConstructor(Class<?> arrayClass)
生成构造所需类型的数组的方法句柄。static MethodHandle
arrayElementGetter(Class<?> arrayClass)
产生方法句柄,提供对数组元素的读取访问。static MethodHandle
arrayElementSetter(Class<?> arrayClass)
生成方法句柄,为数组的元素提供写访问权限。static VarHandle
arrayElementVarHandle(Class<?> arrayClass)
产生一个VarHandle,可以访问arrayClass类型的数组的arrayClass
。static MethodHandle
arrayLength(Class<?> arrayClass)
产生返回数组长度的方法句柄。static VarHandle
byteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder)
产生一个VarHandle,可以访问byte[]
数组的元素,就像它是不同的基元数组类型一样,如int[]
或long[]
。static VarHandle
byteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder)
产生VarHandle给予访问的元素ByteBuffer
视为好像它是一个不同的基本组件类型到的元件的阵列byte
,如int[]
或long[]
。static MethodHandle
catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler)
通过在异常处理程序中运行它来创建适应目标方法句柄的方法句柄。static MethodHandle
collectArguments(MethodHandle target, int pos, MethodHandle filter)
通过使用过滤器(另一种方法句柄)预处理其参数的子序列来适应目标方法句柄。static MethodHandle
constant(Class<?> 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, Class<?>... valueTypes)
产生的方法处理这将调用一些其他指定的 目标方法句柄之前丢弃一些伪参数。static MethodHandle
dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes)
产生的方法处理这将调用一些其他指定的 目标方法句柄之前丢弃一些伪参数。static MethodHandle
dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> 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(Class<?> 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(Class<?> targetClass, MethodHandles.Lookup lookup)
返回一个lookup object
,具有完整功能,可以模拟目标类上的所有支持的字节码行为,包括private access 。static MethodHandles.Lookup
publicLookup()
返回值得信赖的lookup object
。static <T extends Member>
TreflectAs(Class<T> expected, MethodHandle target)
执行一个未经检查的“裂缝”的 direct method handle 。static MethodHandle
spreadInvoker(MethodType type, int leadingArgCount)
产生一个方法句柄,它将调用给定的type
任何方法句柄,给定数量的尾随参数被单个尾随的Object[]
数组替换。static MethodHandle
throwException(Class<?> returnType, Class<? 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(Class<?> type)
生成请求的返回类型的常量方法句柄,每次调用时返回该类型的默认值。
-
-
-
方法详细信息
-
lookup
public static MethodHandles.Lookup lookup()
返回一个lookup object
,具有完整的功能,可以模拟调用者的所有支持的字节码行为。 这些功能包括来电者的private access 。 查找对象上的工厂方法可以为调用者通过字节码(包括受保护和私有字段和方法)访问的任何成员创建direct method handles 。 该查找对象是可以被委托给可信代理的能力 。 不要将其存储在不受信任的代码可以访问的位置。这种方法是调用者敏感的,这意味着它可能会向不同的呼叫者返回不同的值。
对于任何给定的调用者类别
C
,此调用返回的查找对象具有与JVM提供的任何查找对象相同的功能,即在同一调用方类别C
执行的invokedynamic instruction的引导方法。- 结果
- 一个用于此方法的调用者的查找对象,具有私有访问权限
-
publicLookup
public static MethodHandles.Lookup publicLookup()
返回值得信赖的lookup object
。 查找具有PUBLIC
和UNCONDITIONAL
模式。 它只能用于在无条件导出的包中为公共类的公共成员创建方法句柄。作为纯粹的约定,这个查找对象的lookup class将是
Object
。- API Note:
-
Object的使用是常规的,并且由于查找模式有限,所以没有向Object,其包或其模块的内部提供特殊访问。
因此,该查找对象的查找上下文将是引导类加载器,这意味着它无法找到用户类。
讨论:查找类可以使用
C
表达式更改为任何其他类C 。 但是可以通过更改类加载器来更改查找上下文。 公共查找对象总是受到security manager checks的约束 。 另外,它不能访问caller sensitive methods 。 - 结果
- 一个可信赖的查找对象
-
privateLookupIn
public static MethodHandles.Lookup privateLookupIn(Class<?> targetClass, MethodHandles.Lookup lookup) throws IllegalAccessException
返回一个lookup object
,具有完整功能,可以在目标类上模拟所有支持的字节码行为,包括private access 。 此方法检查指定为Lookup
对象的调用者是否允许对目标类进行深层反射 。 如果m1
是包含模块lookup class
,和m2
是包含目标类的模块,则该检查可确保如果有一个安全管理器,它的
checkPermission
方法被调用来检查ReflectPermission("suppressAccessChecks")
。- API Note:
-
MODULE
查找模式用于验证查找对象是由调用者模块中的代码创建的(或从最初由调用者创建的查找对象派生)。 具有MODULE
查找模式的查找对象可以与受信任方共享,而不会给予呼叫者PRIVATE
和PACKAGE
访问。 - 参数
-
targetClass
- 目标类 -
lookup
- 调用者查找对象 - 结果
- 目标类的查找对象,具有私有访问权限
- 异常
-
IllegalArgumentException
- 如果targetClass
是primitve类型或数组类 -
NullPointerException
- 如果targetClass
或caller
是null
-
IllegalAccessException
- 如果以上指定的访问检查失败 -
SecurityException
- 如果安全管理员否认 - 从以下版本开始:
- 9
- 另请参见:
-
MethodHandles.Lookup.dropLookupMode(int)
-
reflectAs
public static <T extends Member> T reflectAs(Class<T> expected, MethodHandle target)
执行一个未经检查的“裂纹”的direct method handle 。 结果就好像用户已经获得了足够能够破解目标方法句柄的查找对象,目标上称为Lookup.revealDirect
,以获取其符号引用,然后称为MethodHandleInfo.reflectAs
来解析对成员的符号引用。如果有安全管理员,它的
checkPermission
方法被调用了一个ReflectPermission("suppressAccessChecks")
权限。- 参数类型
-
T
- 所需类型的结果,Member
或子类型 - 参数
-
target
- 一种直接的方法手柄来破解符号参考组件 -
expected
- 表示所需结果类型T
的类对象 - 结果
- 对方法,构造函数或字段对象的引用
- 异常
-
SecurityException
- 如果呼叫者没有权限调用setAccessible
-
NullPointerException
- 如果任一参数是null
-
IllegalArgumentException
- 如果目标不是直接方法句柄 -
ClassCastException
- 如果成员不是预期类型 - 从以下版本开始:
- 1.8
-
arrayConstructor
public static MethodHandle arrayConstructor(Class<?> arrayClass) throws IllegalArgumentException
生成构造所需类型的数组的方法句柄。 方法句柄的返回类型将是数组类型。 其唯一参数的类型为int
,它指定数组的大小。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个可以创建给定类型的数组的方法句柄
- 异常
-
NullPointerException
- 如果参数是null
-
IllegalArgumentException
- 如果arrayClass
不是数组类型 - 从以下版本开始:
- 9
- 另请参见:
-
Array.newInstance(Class, int)
-
arrayLength
public static MethodHandle arrayLength(Class<?> arrayClass) throws IllegalArgumentException
产生返回数组长度的方法句柄。 方法句柄的类型将具有int
作为返回类型,其唯一的参数将是数组类型。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个方法句柄,可以检索给定数组类型的数组的长度
- 异常
-
NullPointerException
- 如果参数是null
-
IllegalArgumentException
- 如果arrayClass不是数组类型 - 从以下版本开始:
- 9
-
arrayElementGetter
public static MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException
产生方法句柄,提供对数组元素的读取访问。 方法句柄的类型将具有数组的元素类型的返回类型。 它的第一个参数将是数组类型,第二个参数将是int
。- 参数
-
arrayClass
- 数组类型 - 结果
- 一个方法句柄,可以从给定的数组类型加载值
- 异常
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果arrayClass不是数组类型
-
arrayElementSetter
public static MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException
生成方法句柄,为数组的元素提供写访问权限。 方法句柄的类型将具有void返回类型。 它的最后一个参数将是数组的元素类型。 第一个和第二个参数将是数组类型和int。- 参数
-
arrayClass
- 数组的类 - 结果
- 一个可以将值存储到数组类型中的方法句柄
- 异常
-
NullPointerException
- 如果参数为空 -
IllegalArgumentException
- 如果arrayClass不是数组类型
-
arrayElementVarHandle
public static VarHandle arrayElementVarHandle(Class<?> arrayClass) throws IllegalArgumentException
产生一个VarHandle,可以访问arrayClass类型的数组的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)
)。- 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为空 -
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
。一个给定的索引处的字节的访问将导致
IndexOutOfBoundsException
,如果指数小于0
比大于或byte[]
的阵列长度减去的大小(以字节计)T
。对于
T
,索引中的字节访问可能对齐或不对齐,相对于与阵列和索引相关联的底层内存地址A
。 如果访问未对齐,则访问除get
和set
访问模式之外的任何内容将导致IllegalStateException
。 在这种情况下,原子访问只能保证二分之一的最大功率为A
,GCD为A
,而大小(以字节为单位)为T
。 如果访问对齐,则支持以下访问模式,并保证支持原子访问:- 读取所有
T
写访问模式,但访问模式get
和set
用于long
和double
在32位平台上。 - 原子更新的接入方式为
int
,long
,float
或double
。 (JDK的未来主要平台版本可能会支持某些当前不支持的访问模式的其他类型。) -
int
和long
数字原子更新访问模式。 (JDK的未来主要平台版本可能会支持某些当前不支持的访问模式的附加数字类型。) -
int
和long
逐位原子更新访问模式。 (JDK的未来主要平台版本可能会支持某些当前不支持的访问模式的附加数字类型。)
对于
byte[]
阵列,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[]
数组的元素,如同对应于视图数组类的组件类型的元素 - 异常
-
NullPointerException
- 如果viewArrayClass或byteOrder为null -
IllegalArgumentException
- 如果viewArrayClass不是数组类型 -
UnsupportedOperationException
- 如果UnsupportedOperationException
的组件类型不支持作为变量类型 - 从以下版本开始:
- 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
用于读取访问模式之外的任何其他操作。一个给定的索引处的字节的访问将导致
IndexOutOfBoundsException
,如果指数小于0
比大于或ByteBuffer
的限值减去的大小(以字节计)T
。对于
T
,索引中的字节访问可能对齐或不对齐,相对于底层内存地址,A
表示与ByteBuffer
和索引相关联。 如果访问未对齐,则访问除get
和set
访问模式之外的任何内容将导致IllegalStateException
。 在这种情况下的原子访问仅相对于划分的GCD的两个最大功率保证A
的和的大小(以字节计)T
。 如果访问对齐,则支持以下访问模式,并保证支持原子访问:- 读取所有
T
写入访问模式,除了访问模式get
和set
用于long
和double
在32位平台上。 - 原子更新的接入方式为
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
仿佛对应于该部件类型的视图阵列类的元素观察 - 异常
-
NullPointerException
- 如果viewArrayClass或byteOrder为null -
IllegalArgumentException
- 如果viewArrayClass不是数组类型 -
UnsupportedOperationException
- 如果UnsupportedOperationException
的组件类型不支持作为变量类型 - 从以下版本开始:
- 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方法句柄可能很有用。 例如,要模拟一个
invokeExact
调用变量方法句柄M
,提取其类型为T
,查询调用方法X
为T
,并调用invoker方法,如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
的其他前导参数。在调用其目标之前,如果目标与预期的类型不同,则调用者将根据需要应用引用转换,并且将box,unbox或者扩展原始值,如同通过
asType
一样 。 类似地,返回值将根据需要进行转换。 如果目标是variable arity method handle ,则将进行所需的转换,再次如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
的其他前导参数。在调用其目标之前,如果访问模式类型与所需的给定类型不同,则调用者将根据需要应用引用转换,并且将box,unbox或者扩展原始值,如同通过
asType
一样 。 类似地,返回值将根据需要进行转换。此方法等效于以下代码(尽管可能会更有效):
publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)
- 参数
-
accessMode
- VarHandle访问模式 -
type
- 所需的目标类型 - 结果
- 适用于调用其访问模式类型可转换为给定类型的任何VarHandle的访问模式方法的方法句柄。
- 从以下版本开始:
- 9
-
explicitCastArguments
public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType)
生成方法句柄,通过成对参数和返回类型转换将给定方法句柄的类型适配为新类型。 原始类型和新类型必须具有相同数量的参数。 生成的方法句柄保证报告一个等于所需新类型的类型。如果原始类型和新类型相等,则返回目标。
允许使用与
MethodHandle.asType
相同的转换,如果这些转换失败,还会应用一些其他转化。 给定类型T0 , T1 ,如果可能,应用以下转换之一,之前或代替asType
完成的任何转换:- 如果T0和T1是引用,并且T1是接口类型,则类型T0的值作为T1传递,而不进行转换。 (这种接口的处理遵循字节码验证器的使用。)
- 如果T0是布尔值, T1是另一个原语,则布尔值转换为字节值,1为真,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
- 如果任一参数为空 -
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
个传入的参数,其中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
- 如果任何参数为空 -
IllegalArgumentException
- 如果索引数组长度不等于目标的arity,或者如果任何索引数组元素不是newType
的参数的有效索引,或者如果target.type()
和newType
中的两个对应的参数类型不相同,
-
constant
public static MethodHandle constant(Class<?> type, Object value)
生成请求的返回类型的方法句柄,每次调用时返回给定的常量值。在返回方法句柄之前,将传入值转换为请求的类型。 如果请求的类型是原始的,则尝试扩大原始转换,否则尝试参考转换。
返回的方法句柄相当于
identity(type).bindTo(value)
。- 参数
-
type
- 所需方法句柄的返回类型 -
value
- 要返回的值 - 结果
- 给定返回类型的方法句柄,并且没有参数,它始终返回给定的值
- 异常
-
NullPointerException
- 如果type
参数为空 -
ClassCastException
- 如果该值无法转换为所需的返回类型 -
IllegalArgumentException
- 如果给定类型是void.class
-
identity
public static MethodHandle identity(Class<?> type)
生成方法句柄,在调用时返回其唯一参数。- 参数
-
type
- 所需方法句柄的唯一参数和返回值的类型 - 结果
- 接受并返回给定类型的一元方法句柄
- 异常
-
NullPointerException
- 如果参数为空 -
IllegalArgumentException
- 如果给定类型是void.class
-
zero
public static MethodHandle zero(Class<?> type)
生成请求的返回类型的常量方法句柄,每次调用时返回该类型的默认值。 得到的常量方法句柄将没有副作用。返回的方法句柄相当于
empty(methodType(type))
。 它也相当于explicitCastArguments(constant(Object.class, null), methodType(type))
,因为explicitCastArguments
将null
转换为默认值。- 参数
-
type
- 期望的方法句柄的返回类型 - 结果
- 一个常量方法句柄,不带参数并返回给定类型的默认值(如果该类型为void,则为void)
- 异常
-
NullPointerException
- 如果参数为空 - 从以下版本开始:
- 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
- 如果参数为空 - 从以下版本开始:
- 9
- 另请参见:
-
zero(java.lang.Class<?>)
,constant(java.lang.Class<?>, java.lang.Object)
-
insertArguments
public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values)
在方法句柄的调用之前提供一个具有一个或多个绑定参数的目标方法句柄。 对应于绑定参数的目标的形式参数称为绑定参数 。 返回一个新的方法句柄,可以省去绑定的参数。 当它被调用时,它会接收任何非绑定参数的参数,将保存的参数绑定到其对应的参数,并调用原始目标。新方法句柄的类型将从原始目标类型中删除绑定参数的类型,因为新方法句柄将不再需要这些参数由其调用者提供。
每个给定的参数对象必须匹配相应的绑定参数类型。 如果绑定的参数类型是一个原语,则参数对象必须是包装器,并且将被取消装箱以产生原始值。
pos
参数选择要绑定的参数。 它可以在零和NL之间(包括),其中N是目标方法句柄的粗细, L是值数组的长度。注意:即使原始的目标方法句柄是,生成的适配器永远不会是variable-arity method handle 。
- 参数
-
target
- 插入参数后调用的方法句柄 -
pos
- 在哪里插入参数(零为第一个) -
values
- 插入的一系列参数 - 结果
- 在调用原始方法句柄之前插入附加参数的方法句柄
- 异常
-
NullPointerException
- 如果目标或values
数组为空 - 另请参见:
-
MethodHandle.bindTo(java.lang.Object)
-
dropArguments
public static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes)
产生的方法处理这将调用一些其他指定的目标方法句柄之前丢弃一些伪参数。 新方法句柄的类型将与目标类型相同,但在某些给定位置,它也包括虚拟参数类型。pos
参数可以介于零和N之间,其中N是目标的空间。 如果pos
为零,则虚拟参数将在目标的实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, Class<?>... valueTypes)
产生的方法处理这将调用一些其他指定的目标方法句柄之前丢弃一些伪参数。 新方法句柄的类型将与目标类型相同,但在某些给定位置,它也包括虚拟参数类型。pos
参数的范围可以介于零和N之间,其中N是目标的概率。 如果pos
为零,则虚拟参数将在目标的实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
- 如果目标为空,或者如果valueTypes
数组或其任何元素为空 -
IllegalArgumentException
-如果任何元素valueTypes
为void.class
,或者如果pos
比目标的元数负或更大,或者如果新的方法处理的类型将有 too many parameters
-
dropArgumentsToMatch
public static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos)
适应目标方法句柄以匹配给定的参数类型列表。 如有必要,添加虚拟参数。 匹配开始之前可以跳过一些引导参数。target
的参数类型列表中的其余类型必须是起始位置pos
处的newTypes
类型列表的子列表。 所得到的句柄将具有目标句柄的参数类型列表,其中插入到目标原始参数的相应位置中的任何不匹配的参数类型(匹配子列表之前或之后),如同通过dropArguments(MethodHandle, int, Class[])
一样 。所得到的句柄将具有与目标句柄相同的返回类型。
在更正式的术语中,假设这两个类型列表:
- 目标句柄具有参数类型列表
S..., M...
,其中S
类型与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 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
- 如果任一参数为空 -
IllegalArgumentException
-如果任何元件newTypes
是void.class
,或者如果skip
比目标的元数负或更大,或者如果pos
比newTypes列表尺寸负或更大,或者如果newTypes
不包含target
的非跳过位置pos
参数类型。 - 从以下版本开始:
- 9
- 目标句柄具有参数类型列表
-
filterArguments
public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters)
通过预处理其一个或多个参数来适应目标方法句柄,每个参数具有自己的一元过滤器函数,然后使用每个预处理的参数替换为相应过滤器函数的结果来调用目标。预处理由一个或多个方法手柄执行,在
filters
数组的元素中指定。 过滤器阵列的第一个元素对应于目标的pos
参数,依次类推。数组中的空参数被视为身份函数,相应的参数保持不变。 (如果数组中没有非空元素,则返回原始目标。)每个过滤器都应用于适配器的相应参数。
如果过滤器
F
适用于目标的N
参数,那么F
必须是一个方法句柄,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
数组为空 -
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
也必须小于或等于目标的空间。例:
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
消耗一个参数并产生无效结果,则collectArguments(mh, N, coll)
相当于filterArguments(mh, N, coll)
。 其他等价是可能的,但需要参数置换。注意:即使原始的目标方法句柄是,生成的适配器从不是一个variable-arity method handle 。
- 参数
-
target
- 过滤参数子序列后调用的方法句柄 -
pos
- 传递给过滤器的第一个适配器参数的位置和/或接收过滤器结果的目标参数 -
filter
- 方法句柄来调用参数的子序列 - 结果
- 方法句柄,其包含指定的参数子序列过滤逻辑
- 异常
-
NullPointerException
- 如果任一参数为空 -
IllegalArgumentException
-如果的返回类型filter
为非空隙和不一样的pos
目标的参数,或者如果pos
不为0之间以及在目标的元数,包括端值,或者如果产生的方法手柄的类型将具有 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
- 如果任一参数为空 -
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
- 如果任一参数为空 -
IllegalArgumentException
-如果combiner
的返回类型为非空隙和不一样的第一个参数类型的目标,或者如果初始N
参数类型的目标(跳过一个匹配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
- 如果任一参数为空 -
IllegalArgumentException
-如果满足以下两个条件成立:(1)combiner
的返回类型是非void
和不一样在位置参数类型pos
目标签名的; (2)目标签名的位置pos
的N
参数类型(跳过一个匹配combiner
的返回类型)与combiner
的参数类型combiner
。 - 从以下版本开始:
- 9
- 另请参见:
-
foldArguments(MethodHandle, MethodHandle)
-
guardWithTest
public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
使用方法句柄来调整目标方法句柄,通过用测试保护它,一个布尔值方法句柄。 如果防守失败,则会调用后备句柄。 所有三个方法句柄必须具有相同的对应参数和返回类型,除了测试的返回类型必须为布尔值,并且允许测试具有比其他两个方法句柄少的参数。这里是生成的适配器的伪代码。 在代码中,
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
- 用于测试的方法句柄,必须返回布尔值 -
target
- 如果测试通过,调用方法句柄 -
fallback
- 如果测试失败,则调用方法句柄 - 结果
- 方法句柄包含指定的if / then / else逻辑
- 异常
-
NullPointerException
- 如果任何参数为空 -
IllegalArgumentException
- 如果test
不返回布尔值,或者如果所有三种方法类型都不匹配(返回类型为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
不接受给定的异常类型,或者方法句柄类型在其返回类型及其相应参数中不匹配 - 另请参见:
-
tryFinally(MethodHandle, MethodHandle)
-
throwException
public static MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType)
产生一个方法句柄,它将抛出给定的异常exType
。 方法句柄将接受一个单独的参数exType
,并立即将其作为例外。 方法类型将名义上指定返回值为returnType
。 返回类型可能是任何方便的:方法句柄的行为无关紧要,因为它将永远不会正常返回。- 参数
-
returnType
- 所需方法句柄的返回类型 -
exType
- 所需方法句柄的参数类型 - 结果
- 方法句柄可以抛出给定的异常
- 异常
-
NullPointerException
- 如果任一参数为空
-
loop
public static MethodHandle loop(MethodHandle[]... clauses)
构造一个表示循环的方法句柄,该循环具有在每次迭代时更新和检查的多个循环变量。 在由于其中一个谓词终止循环后,运行相应的终结器,并传递循环的结果,循环的结果是结果句柄的返回值。直观地,每个循环由一个或多个“子句”形成,每个“子句”指定本地迭代变量和/或循环出口。 循环的每次迭代按顺序执行每个子句。 一个子句可以选择更新其迭代变量; 它还可以选择执行测试和条件循环退出。 为了在方法句柄方面表达这个逻辑,每个子句最多可以指定四个独立的动作:
- 初始化:在循环执行之前,一个迭代变量的初始化
v
类型的V
。 - 步骤:当一个子句执行时,迭代变量
v
的更新步骤。 - pred:当一个子句执行时,一个谓词执行来测试循环退出。
- fini:如果一个子句导致循环退出,则执行终结器来计算循环的返回值。
(V...)
。 值本身将为(v...)
。 当我们说“参数列表”时,我们通常会指向类型,但在某些上下文(描述执行)中,列表将是实际值。这些条款部分中的一些可以根据某些规则被省略,并且在这种情况下提供有用的默认行为。 详见下文。
参数可选无处不在:每个子句函数是允许的,但不需要接受每个迭代变量
v
的参数。 作为一个例外,init函数不能采用任何v
参数,因为这些值在执行init函数时尚未计算。 任何条款函数都可能忽略其有权采取的参数的任何后续子序列。 实际上,任何子句函数都不会有参数。循环参数:一个子句函数可以使用它所有的迭代变量值,在这种情况下,它也可能需要更多的拖尾参数。 这样的额外值被称为循环参数 ,其类型和值被标记为
(A...)
和(a...)
。 这些将成为所产生的循环句柄的参数,每当执行循环时,它将被提供。 (由于init函数不接受迭代变量v
,init函数的任何参数都将自动为循环参数a
)。与迭代变量一样,子句函数是允许的,但不需要接受循环参数。 这些循环参数用作整个循环中可见的循环不变值。参数可见无处不在:允许每个非init子句函数观察整个循环状态,因为它可以传递当前迭代变量值和传入循环参数的完整列表
(v... a...)
。 init函数可以观察初始的预循环状态,形式为(a...)
。 大多数子句功能不需要所有这些信息,但它们将正式连接到它,就像dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>)
一样 。 更具体地说,我们将使用符号(V*)
来表达一个完整序列的任意前缀(V...)
(且同样(v*)
,(A*)
,(a*)
)。 在该符号中,init函数参数列表的一般形式为(A*)
,非init函数参数列表的一般形式为(V*)
或(V... A*)
。检查子句结构:给定一组子句,执行多个检查和调整,以连接循环的所有部分。 它们在下面的步骤中详细说明。 在这些步骤中,如果循环组合器的输入不满足所需的约束,则单词“必须”的每次出现都对应于将抛出
IllegalArgumentException
的位置。有效地相同的序列: 如果
A
和B
是相同的,或者如果A
较短并且与适当的前缀为B
相同,则参数列表A
被定义为与另一个参数列表B
有效地相同 。 当说出一组无序的参数列表时,我们认为如果集合包含最长列表,则该集合的整体“完全相同”,并且集合的所有成员与最长列表实际上相同。 例如,任何一组的形式的型序列的(V*)
是有效相同的,并且如果窗体的多个序列也是如此(V... A*)
相加。步骤0:确定子句结构。
- 子句数组(型号为
MethodHandle[][]
)必须为非null
并且至少包含一个元素。 - 子句数组可能不包含长于四个元素的
null
或子数组。 - 短于四个元素的条款被视为被填充
null
元素长达四个。 填充通过将元素附加到数组来进行。 - 条款与所有
null
s被忽略。 - 每个子句被视为一个四元组的函数,称为“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函数的参数列表将被调整为外部参数列表。 (请注意,它们的参数列表已经与此列表有效相同)
- 每个非省略,非init(step,pred和fini)函数的参数列表必须与内部参数列表
(V... A...)
有效地相同。
步骤3:填写省略功能。
- 如果省略了一个init函数, 那么对于子句的迭代变量类型,请使用default value 。
- 如果省略步骤功能,请使用该子句的迭代变量类型的identity function ; 在前面的子句的非
void
迭代变量的identity函数之前插入参数参数。 (这将使循环变量变为本地循环不变量。) - 如果省略pred函数,请使用常数
true
函数。 (这将保持循环,就本条而言,请注意,在这种情况下,相应的fini函数是无法访问的。 - 如果省略fini功能,请使用default value作为循环返回类型。
步骤4:填写缺少的参数类型。
- 此时,每个init函数参数列表与外部参数列表
(A...)
有效地相同,但是一些列表可能更短。 对于具有短参数列表的每个初始化函数,请填写列表的末尾。 - 此时,每个非init函数参数列表与内部参数列表
(V... A...)
有效地相同,但是一些列表可能更短。 对于每个具有短参数列表的非init函数,请填写列表的末尾。 - 参数列表由dropping unused trailing arguments填充。
最终观察。
- 在这些步骤之后,通过提供省略的函数和参数来调整所有子句。
- 所有init函数都有一个常见的参数类型列表
(A...)
,最后的循环句柄也将具有。 - 所有fini函数都有一个常见的返回类型
R
,最后的循环句柄也将具有。 - 所有非init函数都有一个公共参数类型列表
(V... A...)
,(非void
)迭代变量V
后跟循环参数。 - 每对init和step函数在返回类型
V
。 - 每个非init函数都能够观察到所有迭代变量的当前值
(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 before pred)执行step和pred函数,直到pred函数返回
false
。 - 来自步骤函数调用的非
void
结果用于更新循环变量序列(v...)
中的相应值。 所有后续的函数调用都会立即显示已更新的值。 - 如果pred函数返回
false
,则调用相应的fini函数,并从循环返回结果值(类型为R
)。 - 如果所有的pred函数总是返回true,那么没有调用fini函数,除了抛出异常,循环不能退出。
使用提示。
- 虽然每个step函数都将接收所有循环变量的当前值,但有时一个step函数只需要观察自己变量的当前值。 在这种情况下,步骤功能可能需要明确地drop all preceding loop variables 。 这将需要提及它们的类型,像
dropArguments(step, 0, V0.class, ...)
这样的表达式。 - 循环变量不需要变化; 它们可以是循环不变的。 一个子句可以通过没有step,pred或者fini函数的合适的init函数创建一个不变的循环。 将传入的循环参数“连接”到相邻循环变量的step或pred函数中可能很有用。
- 如果一些子句函数是实例上的虚拟方法,则实例本身可以方便地放置在初始不变循环“variable”中,使用初始条款,如
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(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>, int, boolean)
一样 。- 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
从参数列表中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
从参数列表中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
从参数列表中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:
-
具有完全符合身体方法的示例:
// 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!"));
,使用最简单的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 ) { 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(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
值,并运行到步长为1的end
(排他)返回的值。如果
body
句柄返回非void
类型的V
,则该类型的前导循环迭代变量也存在。 这个变量是使用初始化的可选init
手柄,或到default value类型的V
如果该句柄是null
。在每次迭代中,迭代变量被传递到调用
body
句柄。 从主体(类型为V
)返回的非void
值更新了前导迭代变量。 环形手柄执行的结果将是最终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
从参数列表中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
,它必须是int
。 见上文其他约束。 -
end
- 一个非null
句柄来返回循环计数器的结束值(循环将运行到end-1
)。 结果类型必须是int
。 见上文其他约束。 -
init
- 可选的初始化程序,提供循环变量的初始值。 可能是null
,意味着默认的初始值。 见上文其他约束。 -
body
- 循环体,可能不是null
。 它在标准情况下控制循环参数和结果类型(详见上文)。 它必须接受自己的返回类型(如果是非int
)加上一个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
,则该类型的前导循环迭代变量也存在。 该变量使用可选的init
句柄进行初始化,或V的V
如果该句柄为null
。在每次迭代中,迭代变量被传递到调用
body
句柄。 从主体(类型V
)返回的非void
值更新前导迭代变量。 环形手柄执行的结果将是最终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
,留下(T A...)V
。 - 身体的参数列表
(V T A...)
对称为内部参数列表的类型的列表做出贡献。 它将限制其他循环部件的参数列表。 - 作为一种特殊情况,如果机构仅提供
V
和T
类型,不附加A
类型,则内部参数列表将通过iterator
句柄的参数类型A...
进行iterator
; 如果是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
。 它在标准情况下控制循环参数和结果类型(详见上文)。 它必须接受自己的返回类型(如果是非T
)加上一个T
参数(对于迭代值),并且可以接受任何数量的其他类型。 见上文其他约束。 - 结果
- 体现迭代循环功能的方法句柄。
- 异常
-
NullPointerException
- 如果body
句柄是null
。 -
IllegalArgumentException
- 如果任何参数违反上述要求。 - 从以下版本开始:
- 9
-
-
tryFinally
public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup)
创建一个方法句柄,通过将其包装在一个try-finally
块中来适应target
方法句柄。 另一个方法手柄,cleanup
,代表finally
块的功能。 在执行target
句柄期间抛出的任何异常都将传递给cleanup
句柄。 除非cleanup
处理会引发异常,否则将被cleanup
抛出异常。 从返回的值cleanup
手柄的执行将是执行的结果try-finally
手柄。cleanup
句柄将被传递一个或两个额外的前导参数。 第一个是在执行target
句柄期间抛出的异常,或者如果没有抛出异常,null
。 第二个是执行target
句柄的结果,或者如果它引发异常,则需要一个null
或false
所需类型的值作为占位符。 如果target
句柄具有void
返回类型,则第二个参数不存在。 (需要注意的是,除了参数类型转换,组合子表示void
通过省略相应的自相矛盾的论点,而不是通过插入在参数列表值null
或零值)。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...
)无法通过执行目标进行修改,如果调用该引用,参数将从调用程序不a...
改为清理。目标和清理必须返回相同的类型,即使清理总是抛出。 要创建这样一个抛出的清理,使用
throwException
编写清理逻辑,以创建正确返回类型的方法句柄。请注意,
tryFinally
不会将异常转换为正常返回。 在极少数情况下,必须以这种方式转换异常,首先用catchException(MethodHandle, Class, MethodHandle)
包装目标以捕获传出的异常,然后用tryFinally
包装。- 参数
-
target
- 其执行将包装在一个try
块中的try
。 -
cleanup
- finally块中调用的句柄。 - 结果
-
一个体现
try-finally
块的方法句柄由两个参数组成。 - 异常
-
NullPointerException
- 如果任何参数为空 -
IllegalArgumentException
- 如果cleanup
不接受所需的前导参数,或者方法句柄类型在其返回类型及其对应的尾随参数中不匹配 - 从以下版本开始:
- 9
- 另请参见:
-
catchException(MethodHandle, Class, MethodHandle)
- 一个
-
-