- Lookup 方法,用于为方法和字段创建方法句柄。
- Combinator 方法,将现有的方法句柄组合或转换为新的方法句柄。
- 其他工厂方法,用于创建模拟其他常见 JVM 操作或控制流模式的方法句柄。
IllegalArgumentException
。
- 自 JDK 版本:
- 1.7
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic final class
一个 查找对象 是一个用于创建方法句柄的工厂,当创建需要访问检查时。 -
Method Summary
Modifier and TypeMethodDescriptionstatic MethodHandle
arrayConstructor
(Class<?> arrayClass) 生成一个方法句柄,构造所需类型的数组,就像使用anewarray
字节码一样。static MethodHandle
arrayElementGetter
(Class<?> arrayClass) 生成一个方法句柄,给予对数组元素的读取访问,就像使用aaload
字节码一样。static MethodHandle
arrayElementSetter
(Class<?> arrayClass) 生成一个方法句柄,给予对数组元素的写入访问,就像使用astore
字节码一样。static VarHandle
arrayElementVarHandle
(Class<?> arrayClass) 生成一个 VarHandle,提供对类型为arrayClass
的数组元素的访问。static MethodHandle
arrayLength
(Class<?> arrayClass) 生成一个方法句柄,返回数组的长度,就像使用arraylength
字节码一样。static VarHandle
byteArrayViewVarHandle
(Class<?> viewArrayClass, ByteOrder byteOrder) 生成一个 VarHandle,提供对byte[]
数组元素的访问,视为不同原始数组类型(如int[]
或long[]
)。static VarHandle
byteBufferViewVarHandle
(Class<?> viewArrayClass, ByteOrder byteOrder) 生成一个 VarHandle,提供对ByteBuffer
视为不同原始组件类型(如int[]
或long[]
)的元素的访问。static MethodHandle
catchException
(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler) 创建一个方法句柄,通过在异常处理程序中运行目标方法句柄来对其进行调整。static <T> T
classData
(MethodHandles.Lookup caller, String name, Class<T> type) 返回与给定caller
查找对象的查找类相关联的类数据,或null
。static <T> T
classDataAt
(MethodHandles.Lookup caller, String name, Class<T> type, int index) static MethodHandle
collectArguments
(MethodHandle target, int pos, MethodHandle filter) 通过使用过滤器(另一个方法句柄)对其参数的子序列进行预处理,从而调整目标方法句柄。static VarHandle
collectCoordinates
(VarHandle target, int pos, MethodHandle filter) 预览。通过使用过滤器(方法句柄)对其坐标值的子序列进行预处理,从而调整目标 VarHandle。static MethodHandle
生成所请求返回类型的方法句柄,每次调用时都返回给定的常量值。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 VarHandle
dropCoordinates
(VarHandle target, int pos, Class<?>... valueTypes) 预览。返回一个 VarHandle,将在委托给目标 VarHandle 之前丢弃一些虚拟坐标。static MethodHandle
dropReturn
(MethodHandle target) 丢弃目标句柄的返回值(如果有)。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 VarHandle
filterCoordinates
(VarHandle target, int pos, MethodHandle... filters) 预览。通过使用一元过滤器函数对其传入坐标值进行预处理,调整目标 VarHandle。static MethodHandle
filterReturnValue
(MethodHandle target, MethodHandle filter) 通过使用过滤器(另一个方法句柄)对其返回值(如果有)进行后处理,调整目标方法句柄。static VarHandle
filterValue
(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) 预览。通过使用一对过滤器函数对其传入和传出值进行预处理,调整目标 VarHandle。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
生成一个方法句柄,调用时返回其唯一参数。static MethodHandle
insertArguments
(MethodHandle target, int pos, Object... values) 在方法句柄调用之前,为目标方法句柄提供一个或多个 绑定参数。static VarHandle
insertCoordinates
(VarHandle target, int pos, Object... values) 预览。在 VarHandle 调用之前,为目标 VarHandle 提供一个或多个 绑定坐标。static MethodHandle
invoker
(MethodType type) 生成一个特殊的 调用器方法句柄,可用于调用与给定类型兼容的任何 VarHandle 的签名多态访问模式方法,就像通过invoke
一样。static MethodHandle
iteratedLoop
(MethodHandle iterator, MethodHandle init, MethodHandle body) 从Iterator<T>
生成的值范围构造一个循环。static MethodHandles.Lookup
lookup()
返回一个具有完整功能的lookup 对象
,以模拟调用者的所有支持的字节码行为。static MethodHandle
loop
(MethodHandle[]... clauses) 构造一个方法句柄,表示具有多个循环变量的循环,这些变量在每次迭代时都会更新和检查。static VarHandle
static MethodHandle
permuteArguments
(MethodHandle target, MethodType newType, int... reorder) 生成一个方法句柄,通过重新排列参数,将给定方法句柄的调用序列调整为新类型。static VarHandle
permuteCoordinates
(VarHandle target, List<Class<?>> newCoordinates, int... reorder) 预览。通过重新排列坐标值,使新坐标与提供的坐标匹配,为目标 VarHandle 提供一个 VarHandle。static MethodHandles.Lookup
privateLookupIn
(Class<?> targetClass, MethodHandles.Lookup caller) static MethodHandles.Lookup
返回一个最小受信任的lookup 对象
。static <T extends Member>
TreflectAs
(Class<T> expected, MethodHandle target) 执行对 直接方法句柄 的未经检查的 "破解"。static MethodHandle
spreadInvoker
(MethodType type, int leadingArgCount) 生成一个方法句柄,每次调用时都将给定数量的尾随参数替换为单个尾随Object[]
数组,并调用给定type
的任何方法句柄。static MethodHandle
tableSwitch
(MethodHandle fallback, MethodHandle... targets) 创建一个表切换方法句柄,可用于根据给定目标索引(称为选择器)在一组目标方法句柄之间切换。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
生成一个所请求返回类型的常量方法句柄,每次调用时都返回该类型的默认值。
-
Method Details
-
lookup
返回一个具有完整功能的lookup 对象
,以模拟调用者的所有支持的字节码行为。这些功能包括对调用者的 完全特权访问。查找对象上的工厂方法可以为调用者具有通过字节码访问的任何成员(包括受保护和私有字段和方法)创建 直接方法句柄。此查找对象由原始查找类创建,并设置了ORIGINAL
位。此查找对象是一个 能力,可以委托给受信任的代理。不要将其存储在未经信任的代码可以访问的位置。此方法是调用者敏感的,这意味着它可能对不同的调用者返回不同的值。在没有调用者帧的情况下调用
MethodHandles.lookup
的情况下(例如,直接从 JNI 附加线程调用时),将抛出IllegalCallerException
。要在这种情况下获取lookup 对象
,请使用一个将隐式标识为调用者的辅助类,或使用publicLookup()
来获取低特权查找。- 返回:
- 为调用此方法的调用者返回一个查找对象,具有原始和完全特权访问。
- 抛出:
-
IllegalCallerException
- 如果堆栈上没有调用者帧。
-
publicLookup
- API 注意:
-
使用Object是传统的,由于查找模式受限,因此不提供对Object、其包或其模块的特殊访问。此公共查找对象或其他具有
UNCONDITIONAL
模式的查找对象假定可读性。因此,查找类不用于确定查找上下文。讨论: 可以使用形式为
publicLookup().in(C.class)
的表达式将查找类更改为任何其他类C。公共查找对象始终受到安全管理器检查。此外,它无法访问调用者敏感方法。 - 返回:
- 一个受最低信任的查找对象
-
privateLookupIn
public static MethodHandles.Lookup privateLookupIn(Class<?> targetClass, MethodHandles.Lookup caller) throws IllegalAccessException 返回一个查找
对象,用于模拟所有支持的字节码行为,包括私有访问。返回的查找对象可以访问模块和包中的类以及这些类的成员,而不受Java访问控制的正常规则的限制,而是符合模块化的更宽松的深度反射规则。指定为
Lookup
对象的调用者在模块M1
中允许对模块M2
和目标类的包进行深度反射,当且仅当以下所有条件都为true
时:- 如果存在安全管理器,则将调用其
checkPermission
方法来检查ReflectPermission("suppressAccessChecks")
,并且必须正常返回。 - 调用者查找对象必须具有完全特权访问。具体来说:
- 目标类必须是一个合适的类,而不是原始类或数组类。(因此,
M2
是明确定义的。) - 如果调用模块
M1
与目标模块M2
不同,则必须同时满足以下两个条件:
如果违反上述任何检查,此方法将引发异常。
否则,如果
M1
和M2
是同一模块,则此方法返回具有null
前一个查找类的完全特权访问的Lookup
对象。否则,
M1
和M2
是两个不同的模块。此方法返回在targetClass
上的Lookup
,将调用者的查找类记录为新的前一个查找类,具有PRIVATE
访问但没有MODULE
访问。生成的
Lookup
对象没有ORIGINAL
访问。- API 注意:
-
此方法返回的
Lookup
对象允许定义在targetClass
的运行时包中的类。在向另一个模块打开包时应格外小心,因为这样定义的类具有与targetClass
模块中的其他成员相同的完全特权访问。 - 参数:
-
targetClass
- 目标类 -
caller
- 调用者查找对象 - 返回:
- 一个用于目标类的查找对象,具有私有访问
- 抛出:
-
IllegalArgumentException
- 如果targetClass
是原始类型或void或数组类 -
NullPointerException
- 如果targetClass
或caller
为null
-
SecurityException
- 如果安全管理器拒绝 -
IllegalAccessException
- 如果违反上述任何其他访问检查 - 自:
- 9
- 参见:
- 如果存在安全管理器,则将调用其
-
classData
public static <T> T classData(MethodHandles.Lookup caller, String name, Class<T> type) throws IllegalAccessException Returns the class data associated with the lookup class of the givencaller
lookup object, ornull
.A hidden class with class data can be created by calling
Lookup::defineHiddenClassWithClassData
. This method will cause the static class initializer of the lookup class of the givencaller
lookup object be executed if it has not been initialized.A hidden class created by
Lookup::defineHiddenClass
and non-hidden classes have no class data.null
is returned if this method is called on the lookup object on these classes.The lookup modes for this lookup must have original access in order to retrieve the class data.
- API Note:
-
This method can be called as a bootstrap method for a dynamically computed constant. A framework can create a hidden class with class data, for example that can be
Class
orMethodHandle
object. The class data is accessible only to the lookup object created by the original caller but inaccessible to other members in the same nest. If a framework passes security sensitive objects to a hidden class via class data, it is recommended to load the value of class data as a dynamically computed constant instead of storing the class data in private static field(s) which are accessible to other nestmates. - Type Parameters:
-
T
- the type to cast the class data object to - Parameters:
-
caller
- the lookup context describing the class performing the operation (normally stacked by the JVM) -
name
- must beConstantDescs.DEFAULT_NAME
("_"
) -
type
- the type of the class data - Returns:
-
the value of the class data if present in the lookup class; otherwise
null
- Throws:
-
IllegalArgumentException
- if name is not"_"
-
IllegalAccessException
- if the lookup context does not have original access -
ClassCastException
- if the class data cannot be converted to the giventype
-
NullPointerException
- ifcaller
ortype
argument isnull
- See Java Virtual Machine Specification:
-
5.5 Initialization
- Since:
- 16
- See Also:
-
classDataAt
public static <T> T classDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index) throws IllegalAccessException 返回指定索引处的元素在类数据中,如果与给定caller
查找对象的查找类相关联的类数据是List
。如果在此查找类中不存在类数据,则此方法返回null
。可以通过调用
Lookup::defineHiddenClassWithClassData
创建具有类数据的隐藏类。如果尚未初始化给定caller
查找对象的查找类的静态类初始化程序,则此方法将导致其执行。通过
Lookup::defineHiddenClass
创建的隐藏类和非隐藏类没有类数据。如果在这些类上调用此方法,则返回null
。- API 注意:
-
此方法可以作为动态计算的常量的引导方法调用。框架可以创建具有类数据的隐藏类,例如可以是
List.of(o1, o2, o3....)
包含多个对象,并使用此方法在特定索引处加载一个元素。类数据仅对由原始调用者创建的查找对象可访问,但对同一巢层中的其他成员不可访问。如果框架通过类数据将安全敏感对象传递给隐藏类,建议将类数据的值作为动态计算的常量加载,而不是将类数据存储在对其他巢层成员可访问的私有静态字段中。 - 类型参数:
-
T
- 要将结果对象转换为的类型 - 参数:
-
caller
- 描述执行操作的类的查找上下文(通常由JVM堆栈) -
name
- 必须是ConstantDescs.DEFAULT_NAME
("_"
) -
type
- 类数据中给定索引处的元素的类型 -
index
- 类数据中元素的索引 - 返回:
-
如果类数据存在,则返回类数据中给定索引处的元素;否则返回
null
- 抛出:
-
IllegalArgumentException
- 如果名称不是"_"
-
IllegalAccessException
- 如果查找上下文没有原始访问权限 -
ClassCastException
- 如果类数据无法转换为List
或指定索引处的元素无法转换为给定类型 -
IndexOutOfBoundsException
- 如果索引超出范围 -
NullPointerException
- 如果caller
或type
参数为null
;或者如果解箱操作失败,因为给定索引处的元素为null
- 自:
- 16
- 另请参见:
-
reflectAs
执行对direct method handle
的未检查“破解”。结果就好像用户已经获得了足够能够破解目标方法句柄的查找对象,调用了目标上的Lookup.revealDirect
以获取其符号引用,然后调用MethodHandleInfo.reflectAs
来将符号引用解析为成员。如果存在安全管理器,则将使用
ReflectPermission("suppressAccessChecks")
权限调用其checkPermission
方法。- 类型参数:
-
T
- 结果的期望类型,可以是Member
或其子类型 - 参数:
-
expected
- 表示所需结果类型T
的类对象 -
target
- 要破解为符号引用组件的直接方法句柄 - 返回:
- 方法、构造函数或字段对象的引用
- 抛出:
-
SecurityException
- 如果调用者无权调用setAccessible
-
NullPointerException
- 如果任一参数为null
-
IllegalArgumentException
- 如果目标不是直接方法句柄 -
ClassCastException
- 如果成员不是期望的类型 - 自:
- 1.8
-
arrayConstructor
生成一个构造所需类型数组的方法句柄,就好像通过anewarray
字节码一样。方法句柄的返回类型将是数组类型。其唯一参数的类型将是int
,指定数组的大小。如果返回的方法句柄使用负数组大小调用,则将抛出
NegativeArraySizeException
。- 参数:
-
arrayClass
- 一个数组类型 - 返回:
- 一个可以创建给定类型数组的方法句柄
- 抛出:
-
NullPointerException
- 如果参数为null
-
IllegalArgumentException
- 如果arrayClass
不是数组类型 - 参见Java虚拟机规范:
-
6.5
anewarray
指令
- 自:
- 9
- 另请参见:
-
arrayLength
生成一个返回数组长度的方法句柄,就好像通过arraylength
字节码一样。方法句柄的类型将具有int
作为返回类型,其唯一参数将是数组类型。如果返回的方法句柄使用
null
数组引用调用,则将抛出NullPointerException
。- 参数:
-
arrayClass
- 一个数组类型 - 返回:
- 一个可以检索给定数组类型数组长度的方法句柄
- 抛出:
-
NullPointerException
- 如果参数为null
-
IllegalArgumentException
- 如果arrayClass
不是数组类型 - 参见Java虚拟机规范:
-
6.5
arraylength
指令
- 自:
- 9
-
arrayElementGetter
生成一个方法句柄,允许对数组元素进行读取访问,就好像通过aaload
字节码一样。方法句柄的类型将具有数组的元素类型作为返回类型。其第一个参数将是数组类型,第二个参数将是int
。当调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用为
null
,将抛出NullPointerException
;如果索引为负数或大于等于数组长度,则将抛出ArrayIndexOutOfBoundsException
。- 参数:
-
arrayClass
- 一个数组类型 - 返回:
- 一个可以从给定数组类型加载值的方法句柄
- 抛出:
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果arrayClass
不是数组类型 - 参见Java虚拟机规范:
-
6.5
aaload
指令
-
arrayElementSetter
生成一个方法句柄,允许对数组元素进行写入访问,就好像通过astore
字节码一样。方法句柄的类型将具有void返回类型。其最后一个参数将是数组的元素类型。第一个和第二个参数将是数组类型和int。当调用返回的方法句柄时,将检查数组引用和数组索引。如果数组引用为
null
,将抛出NullPointerException
;如果索引为负数或大于等于数组长度,则将抛出ArrayIndexOutOfBoundsException
。- 参数:
-
arrayClass
- 数组的类 - 返回:
- 一个方法句柄,可以将值存储到数组类型中
- 抛出:
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果arrayClass不是数组类型 - 参见 Java虚拟机规范:
-
6.5
aastore
指令
-
arrayElementVarHandle
生成一个VarHandle,用于访问类型为arrayClass
的数组的元素。VarHandle的变量类型是arrayClass
的组件类型,坐标类型列表为(arrayClass, int)
,其中int
坐标类型对应于作为数组索引的参数。返回的VarHandle的某些访问模式在以下情况下不受支持:
- 如果组件类型不是
byte
,short
,char
,int
,long
,float
, 或double
,则不支持数值原子更新访问模式。 - 如果组件类型不是
boolean
,byte
,short
,char
,int
, 或long
,则不支持位原子更新访问模式。
如果组件类型是
float
或double
,则数值和原子更新访问模式将使用它们的位表示进行比较(参见Float.floatToRawIntBits(float)
和Double.doubleToRawLongBits(double)
)。当调用返回的
VarHandle
时,将检查数组引用和数组索引。如果数组引用为null
,将抛出NullPointerException
,如果索引为负数或大于等于数组长度,则将抛出ArrayIndexOutOfBoundsException
。- API 注意:
-
float
值或double
值的位比较,如数值和原子更新访问模式所执行的,与原始的==
运算符以及Float.equals(java.lang.Object)
和Double.equals(java.lang.Object)
方法有所不同,特别是在比较 NaN 值或比较-0.0
和+0.0
。在使用这些值进行比较和设置或交换操作时应谨慎,因为操作可能会意外失败。在Java中有许多可能被视为NaN
的 NaN 值,尽管Java提供的任何IEEE 754浮点运算都无法区分它们。如果期望值或参考值是 NaN 值,并且它被转换(可能以特定于平台的方式)为另一个 NaN 值,从而具有不同的位表示(参见Float.intBitsToFloat(int)
或Double.longBitsToDouble(long)
了解更多详情)。值-0.0
和+0.0
具有不同的位表示,但在使用原始的==
运算符时被视为相等。例如,如果数值算法计算期望值为-0.0
,并且先前计算的参考值为+0.0
,则操作可能会失败。 - 参数:
-
arrayClass
- 数组的类,类型为T[]
- 返回:
- 一个VarHandle,用于访问数组的元素
- 抛出:
-
NullPointerException
- 如果 arrayClass 为 null -
IllegalArgumentException
- 如果 arrayClass 不是数组类型 - 自 JDK 版本:
- 9
- 如果组件类型不是
-
byteArrayViewVarHandle
public static VarHandle byteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException 生成一个VarHandle,用于访问将byte[]
数组视为不同原始数组类型(如int[]
或long[]
)的元素。VarHandle的变量类型是viewArrayClass
的组件类型,坐标类型列表为(byte[], int)
,其中int
坐标类型对应于作为byte[]
数组索引的参数。返回的VarHandle访问byte[]
数组中的索引处的字节,根据给定的字节序将字节组合到或从viewArrayClass
的组件类型的值中。支持的组件类型(变量类型)为
short
,char
,int
,long
,float
和double
。访问给定索引处的字节将导致
ArrayIndexOutOfBoundsException
,如果索引小于0
或大于byte[]
数组长度减去T
的大小(以字节为单位)。对于
T
,在与数组关联的底层内存地址A
相对于对齐或未对齐的情况下,对索引处的字节进行访问。如果访问未对齐,则除了get
和set
访问模式之外,其他访问模式将导致IllegalStateException
。在这种情况下,只能保证相对于A
和T
大小(以字节为单位)的最大二的幂的最大公约数。如果访问对齐,则支持以下访问模式,并保证支持原子访问:- 对于所有
T
,读写访问模式,但在32位平台上,对于long
和double
的get
和set
访问模式除外。 int
,long
,float
或double
的原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他类型的特定访问模式。)int
和long
的数值原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他数值类型的特定访问模式。)int
和long
的位原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不支持的其他数值类型的特定访问模式。)
未对齐访问,因此原子性保证,可以在不操作特定数组的情况下确定
byte[]
数组。给定一个index
,T
及其对应的装箱类型T_BOX
,可以如下确定未对齐:int sizeOfT = T_BOX.BYTES; // 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
- 如果 viewArrayClass 的组件类型不受支持作为变量类型 - 自 JDK 版本:
- 9
- 对于所有
-
byteBufferViewVarHandle
public static VarHandle byteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException 生成一个VarHandle,用于访问将ByteBuffer
的元素视为不同原始组件类型的数组的元素,例如int[]
或long[]
。VarHandle的变量类型是viewArrayClass
的组件类型,坐标类型列表为(ByteBuffer, int)
,其中int
坐标类型对应于作为byte[]
数组索引的参数。返回的VarHandle访问ByteBuffer
中的索引处的字节,根据给定的字节顺序将字节组合到或从viewArrayClass
的组件类型的值中。支持的组件类型(变量类型)包括
short
、char
、int
、long
、float
和double
。如果
ByteBuffer
是只读的,除了读取访问模式之外的任何访问都会导致ReadOnlyBufferException
。访问给定索引处的字节将导致
IndexOutOfBoundsException
,如果索引小于0
或大于ByteBuffer
限制减去T
的大小(以字节为单位)。对于
T
,访问给定索引处的字节可能是对齐的或未对齐的,相对于与ByteBuffer
和索引关联的底层内存地址A
。如果访问未对齐,则除了get
和set
访问模式之外的任何访问都会导致IllegalStateException
。在这种情况下,原子访问仅针对A
的最大二的幂与T
的大小(以字节为单位)的最大公约数进行保证。如果访问是对齐的,则支持以下访问模式,并保证支持原子访问:- 对于所有
T
,读写访问模式,但在32位平台上除了long
和double
的get
和set
访问模式。 int
、long
、float
或double
的原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他类型。)int
和long
的数值原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他数值类型。)int
和long
的位原子更新访问模式。(JDK的未来主要平台版本可能支持某些当前不受支持的访问模式的其他数值类型。)
对于
ByteBuffer
、bb
(直接或其他方式)、index
、T
及其对应的装箱类型T_BOX
,可以确定未对齐访问和因此原子性保证如下:int sizeOfT = T_BOX.BYTES; // 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
- 如果viewArrayClass的组件类型不受支持作为变量类型 - 自:
- 9
- 对于所有
-
spreadInvoker
生成一个方法句柄,该句柄将调用给定type
的任何方法句柄,其中给定数量的尾随参数将被替换为单个尾随Object[]
数组。生成的调用程序将是一个具有以下参数的方法句柄:- 一个单一的
MethodHandle
目标 - 零个或多个前导值(由
leadingArgCount
计数) - 包含尾随参数的
Object[]
数组
调用程序将像调用
invoke
一样调用其目标,使用指定的type
。也就是说,如果目标恰好是给定的type
,它将表现得像invokeExact
;否则,它将表现得像asType
用于将目标转换为所需的type
。返回的调用程序的类型不会是给定的
type
,而是除了前leadingArgCount
个参数外,所有参数都将替换为单个Object[]
类型的数组,这将是最终参数。在调用其目标之前,调用程序将展开最终数组,根据需要应用引用转换,并取消装箱和扩展原始参数。如果在调用调用程序时提供的数组参数的元素数量不正确,则调用程序将抛出一个
IllegalArgumentException
,而不是调用目标。此方法等效于以下代码(尽管可能更有效):
MethodHandle invoker = MethodHandles.invoker(type); int spreadArgCount = type.parameterCount() - leadingArgCount; invoker = invoker.asSpreader(Object[].class, spreadArgCount); return invoker;
- 参数:
-
type
- 所需的目标类型 -
leadingArgCount
- 要传递给目标的固定参数数量 - 返回:
- 适用于调用给定类型的任何方法句柄的方法句柄
- 抛出:
-
NullPointerException
- 如果type
为null -
IllegalArgumentException
- 如果leadingArgCount
不在从0到type.parameterCount()
(包括)的范围内,或者如果生成的方法句柄的类型将具有太多参数
- 一个单一的
-
exactInvoker
生成一个特殊的调用程序方法句柄,可用于调用给定类型的任何方法句柄,就像通过invokeExact
一样。生成的调用程序将具有与所需类型完全相同的类型,只是它将接受一个额外的MethodHandle
类型的前导参数。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
讨论:调用程序方法句柄在处理未知类型的可变方法句柄时可能很有用。例如,要模拟对变量方法句柄
M
进行invokeExact
调用,提取其类型T
,查找T
的调用程序方法X
,并调用调用程序方法,如X.invoke(T, A...)
。 (调用X.invokeExact
不起作用,因为类型T
是未知的。)如果需要传播、收集或其他参数转换,则可以一次应用于调用程序X
并在许多M
方法句柄值上重用,只要它们与X
的类型兼容。(注意:调用程序方法不可通过核心反射API获得。尝试在声明的
invokeExact
或invoke
方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException
。)此方法不会抛出反射或安全异常。
- 参数:
-
type
- 所需的目标类型 - 返回:
- 适用于调用给定类型的任何方法句柄的方法句柄
- 抛出:
-
IllegalArgumentException
- 如果生成的方法句柄的类型将具有太多参数
-
invoker
生成一个特殊的调用器方法句柄,可用于调用与给定类型兼容的任何方法句柄,就像通过invoke
一样。生成的调用器将具有与所需类型完全相同的类型,只是它将接受一个额外的类型为MethodHandle
的前导参数。在调用其目标之前,如果目标与期望的类型不同,调用器将根据需要应用引用转换,并对原始值进行装箱、拆箱或扩展,就像通过
asType
一样。类似地,返回值将根据需要进行转换。如果目标是一个可变参数方法句柄,则将进行所需的参数数量转换,再次如同通过asType
一样。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(MethodHandle.class, "invoke", type)
讨论:通用方法类型是仅提及
Object
参数和返回值的方法类型。对于这种类型的调用器能够调用与通用类型相同参数数量的任何方法句柄。(注意:调用器方法不可通过核心反射API获得。尝试在声明的
invokeExact
或invoke
方法上调用java.lang.reflect.Method.invoke将引发UnsupportedOperationException
。)此方法不会抛出反射或安全异常。
- 参数:
-
type
- 所需的目标类型 - 返回:
- 一个适用于调用任何可转换为给定类型的方法句柄的方法句柄
- 抛出:
-
IllegalArgumentException
- 如果生成的方法句柄的类型将具有太多参数
-
varHandleExactInvoker
生成一个特殊的调用器方法句柄,可用于在与给定类型兼容的任何VarHandle上调用签名多态访问模式方法。生成的调用器将具有与所需给定类型完全相同的类型,只是它将接受一个额外的类型为VarHandle
的前导参数。- 参数:
-
accessMode
- VarHandle访问模式 -
type
- 所需的目标类型 - 返回:
- 一个适用于调用任何access mode类型与给定类型兼容的VarHandle的访问模式方法的方法句柄
- 自JDK版本:
- 9
-
varHandleInvoker
生成一个特殊的调用器方法句柄,可用于在与给定类型兼容的任何VarHandle上调用签名多态访问模式方法。生成的调用器将具有与所需给定类型完全相同的类型,只是它将接受一个额外的类型为VarHandle
的前导参数。在调用其目标之前,如果访问模式类型与所需给定类型不同,调用器将根据需要应用引用转换,并对原始值进行装箱、拆箱或扩展,就像通过
asType
一样。类似地,返回值将根据需要进行转换。此方法等效于以下代码(尽管可能更有效):
publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)
- 参数:
-
accessMode
- VarHandle访问模式 -
type
- 所需的目标类型 - 返回:
- 一个适用于调用任何access mode类型可转换为给定类型的VarHandle的访问模式方法的方法句柄
- 自JDK版本:
- 9
-
explicitCastArguments
Produces a method handle which adapts the type of the given method handle to a new type by pairwise argument and return type conversion. The original type and new type must have the same number of arguments. The resulting method handle is guaranteed to report a type which is equal to the desired new type.If the original type and new type are equal, returns target.
The same conversions are allowed as for
MethodHandle.asType
, and some additional conversions are also applied if those conversions fail. Given types T0, T1, one of the following conversions is applied if possible, before or instead of any conversions done byasType
:- If T0 and T1 are references, and T1 is an interface type, then the value of type T0 is passed as a T1 without a cast. (This treatment of interfaces follows the usage of the bytecode verifier.)
- If T0 is boolean and T1 is another primitive, the boolean is converted to a byte value, 1 for true, 0 for false. (This treatment follows the usage of the bytecode verifier.)
- If T1 is boolean and T0 is another primitive, T0 is converted to byte via Java casting conversion (JLS 5.5), and the low order bit of the result is tested, as if by
(x & 1) != 0
. - If T0 and T1 are primitives other than boolean, then a Java casting conversion (JLS 5.5) is applied. (Specifically, T0 will convert to T1 by widening and/or narrowing.)
- If T0 is a reference and T1 a primitive, an unboxing conversion will be applied at runtime, possibly followed by a Java casting conversion (JLS 5.5) on the primitive value, possibly followed by a conversion from byte to boolean by testing the low-order bit.
- If T0 is a reference and T1 a primitive, and if the reference is null at runtime, a zero value is introduced.
- Parameters:
-
target
- the method handle to invoke after arguments are retyped -
newType
- the expected type of the new method handle - Returns:
- a method handle which delegates to the target after performing any necessary argument conversions, and arranges for any necessary return value conversions
- Throws:
-
NullPointerException
- if either argument is null -
WrongMethodTypeException
- if the conversion cannot be made - See Also:
-
permuteArguments
public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) Produces a method handle which adapts the calling sequence of the given method handle to a new type, by reordering the arguments. The resulting method handle is guaranteed to report a type which is equal to the desired new type.The given array controls the reordering. Call
#I
the number of incoming parameters (the valuenewType.parameterCount()
, and call#O
the number of outgoing parameters (the valuetarget.type().parameterCount()
). Then the length of the reordering array must be#O
, and each element must be a non-negative number less than#I
. For everyN
less than#O
, theN
-th outgoing argument will be taken from theI
-th incoming argument, whereI
isreorder[N]
.No argument or return value conversions are applied. The type of each incoming argument, as determined by
newType
, must be identical to the type of the corresponding outgoing parameter or parameters in the target method handle. The return type ofnewType
must be identical to the return type of the original target.The reordering array need not specify an actual permutation. An incoming argument will be duplicated if its index appears more than once in the array, and an incoming argument will be dropped if its index does not appear in the array. As in the case of
dropArguments
, incoming arguments which are not mentioned in the reordering array may be of any type, as determined only bynewType
.import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodType intfn1 = methodType(int.class, int.class); MethodType intfn2 = methodType(int.class, int.class, int.class); MethodHandle sub = ... (int x, int y) -> (x-y) ...; assert(sub.type().equals(intfn2)); MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1); MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0); assert((int)rsub.invokeExact(1, 100) == 99); MethodHandle add = ... (int x, int y) -> (x+y) ...; assert(add.type().equals(intfn2)); MethodHandle twice = permuteArguments(add, intfn1, 0, 0); assert(twice.type().equals(intfn1)); assert((int)twice.invokeExact(21) == 42);
Note: The resulting adapter is never a variable-arity method handle, even if the original target method handle was.
- Parameters:
-
target
- the method handle to invoke after arguments are reordered -
newType
- the expected type of the new method handle -
reorder
- an index array which controls the reordering - Returns:
- a method handle which delegates to the target after it drops unused arguments and moves and/or duplicates the other arguments
- Throws:
-
NullPointerException
- if any argument is null -
IllegalArgumentException
- if the index array length is not equal to the arity of the target, or if any index array element not a valid index for a parameter ofnewType
, or if two corresponding parameter types intarget.type()
andnewType
are not identical,
-
constant
生成一个方法句柄,每次调用时都返回给定的常量值,并将传入的值转换为请求的类型。如果请求的类型是原始类型,则尝试进行扩展原始类型转换,否则尝试进行引用转换。 返回的方法句柄等同于identity(type).bindTo(value)
。- 参数:
-
type
- 所需方法句柄的返回类型 -
value
- 要返回的值 - 返回:
- 一个给定返回类型和无参数的方法句柄,每次都返回给定值
- 抛出:
-
NullPointerException
- 如果type
参数为null -
ClassCastException
- 如果值无法转换为所需的返回类型 -
IllegalArgumentException
- 如果给定类型为void.class
-
identity
生成一个方法句柄,调用时返回其唯一参数。- 参数:
-
type
- 所需方法句柄的唯一参数和返回值的类型 - 返回:
- 一个接受并返回给定类型的一元方法句柄
- 抛出:
-
NullPointerException
- 如果参数为null -
IllegalArgumentException
- 如果给定类型为void.class
-
zero
生成一个请求的返回类型的常量方法句柄,每次调用时返回该类型的默认值。生成的常量方法句柄不会产生副作用。 返回的方法句柄等同于empty(methodType(type))
。它也等同于explicitCastArguments(constant(Object.class, null), methodType(type))
,因为explicitCastArguments
将null
转换为默认值。- 参数:
-
type
- 所需方法句柄的预期返回类型 - 返回:
- 一个常量方法句柄,不带参数,返回给定类型的默认值(如果类型为void,则返回void)
- 抛出:
-
NullPointerException
- 如果参数为null - 自 JDK 9 起:
- 9
- 参见:
-
empty
生成一个请求类型的方法句柄,忽略任何参数,不执行任何操作,并根据返回类型返回适当的默认值。即返回零原始值、null
或void
。 返回的方法句柄等同于dropArguments(zero(type.returnType()), 0, type.parameterList())
。- API 注意:
-
给定一个谓词和目标,可以生成一个有用的“if-then”构造,如
guardWithTest(pred, target, empty(target.type())
。 - 参数:
-
type
- 所需方法句柄的类型 - 返回:
- 一个给定类型的常量方法句柄,返回给定返回类型的默认值
- 抛出:
-
NullPointerException
- 如果参数为null - 自 JDK 9 起:
- 9
- 参见:
-
insertArguments
提供一个目标方法句柄,提前绑定一个或多个绑定参数。与绑定参数对应的目标的形式参数称为绑定参数。返回一个新的方法句柄,保存绑定参数。当调用它时,为任何非绑定参数接收参数,将保存的参数绑定到其对应的参数,并调用原始目标。 新方法句柄的类型将从原始目标类型中删除绑定参数的类型,因为新方法句柄将不再需要调用者提供这些参数。 每个给定的参数对象必须与相应的绑定参数类型匹配。如果绑定参数类型是原始类型,则参数对象必须是包装器,并将被拆箱以产生原始值。pos
参数选择要绑定的参数。它可以在零和N-L(包括)之间变化,其中N是目标方法句柄的元数,L是值数组的长度。 注意: 结果适配器永远不会是一个可变元方法句柄,即使原始目标方法句柄是。- 参数:
-
target
- 插入参数后要调用的方法句柄 -
pos
- 要插入参数的位置(第一个为零) -
values
- 要插入的参数系列 - 返回:
- 一个插入额外参数后调用原始方法句柄的方法句柄
- 抛出:
-
NullPointerException
- 如果目标或values
数组为null -
IllegalArgumentException
- 如果pos
小于0
或大于N - L
,其中N
是目标方法句柄的元数,L
是值数组的长度。 -
ClassCastException
- 如果参数与相应的绑定参数类型不匹配。 - 参见:
-
dropArguments
生成一个方法句柄,在调用其他指定的目标方法句柄之前丢弃一些虚拟参数。新方法句柄的类型将与目标的类型相同,只是还包括虚拟参数类型,位于某个给定位置。pos
参数可以在零和N之间变化,其中N是目标的元数。如果pos
为零,则虚拟参数将在目标的真实参数之前;如果pos
为N,则它们将在后面。示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); assertEquals("xy", (String) cat.invokeExact("x", "y")); MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class); MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2)); assertEquals(bigType, d0.type()); assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
此方法也等同于以下代码:
dropArguments
(target, pos, valueTypes.toArray(new Class[0]))
- 参数:
-
target
- 在删除参数后要调用的方法句柄 -
pos
- 要删除第一个参数的位置(最左边为零) -
valueTypes
- 要删除的参数的类型 - 返回:
- 一个删除给定类型参数后调用原始方法句柄的方法句柄
- 抛出:
-
NullPointerException
- 如果目标为null,或者valueTypes
列表或其任何元素为null -
IllegalArgumentException
- 如果valueTypes
的任何元素为void.class
,或者pos
为负数或大于目标的元数,或者新方法句柄的类型将具有太多参数
-
dropArguments
生成一个方法句柄,在调用其他指定的目标方法句柄之前丢弃一些虚拟参数。新方法句柄的类型将与目标的类型相同,只是还包括虚拟参数类型,位于某个给定位置。pos
参数可以在零和N之间变化,其中N是目标的元数。如果pos
为零,则虚拟参数将在目标的真实参数之前;如果pos
为N,则它们将在后面。- API 注释:
-
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); assertEquals("xy", (String) cat.invokeExact("x", "y")); MethodHandle d0 = dropArguments(cat, 0, String.class); assertEquals("yz", (String) d0.invokeExact("x", "y", "z")); MethodHandle d1 = dropArguments(cat, 1, String.class); assertEquals("xz", (String) d1.invokeExact("x", "y", "z")); MethodHandle d2 = dropArguments(cat, 2, String.class); assertEquals("xy", (String) d2.invokeExact("x", "y", "z")); MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
此方法也等同于以下代码:
dropArguments
(target, pos, Arrays.asList(valueTypes))
- 参数:
-
target
- 在删除参数后要调用的方法句柄 -
pos
- 要删除的第一个参数的位置(最左边为零) -
valueTypes
- 要删除的参数的类型 - 返回:
- 一个方法句柄,它在调用原始方法句柄之前删除给定类型的参数
- 抛出:
-
NullPointerException
- 如果目标为空,或者valueTypes
数组或其任何元素为空 -
IllegalArgumentException
- 如果valueTypes
的任何元素是void.class
,或者pos
为负数或大于目标的参数数量,或者新方法句柄的类型将具有太多参数
-
dropArgumentsToMatch
public static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos) 将目标方法句柄调整为匹配给定参数类型列表。如果需要,会添加虚拟参数。在匹配开始之前,可以跳过一些前导参数。目标的参数类型列表中剩余的类型必须是newTypes
类型列表的子列表,起始位置为pos
。生成的句柄将具有目标句柄的参数类型列表,其中任何不匹配的参数类型(在匹配子列表之前或之后)将按照相应位置插入到目标原始参数中,就像通过dropArguments(MethodHandle, int, Class[])
一样。生成的句柄将具有与目标句柄相同的返回类型。
更正式地说,假设有这两个类型列表:
- 目标句柄具有参数类型列表
S..., M...
,其中S
中的类型数量由skip
指示。M
类型是应该与给定类型列表newTypes
的一部分匹配的类型。 newTypes
列表包含类型P..., M..., A...
,其中P
中的类型数量由pos
指示。M
类型恰好是目标句柄的参数类型列表中应该匹配的类型。A
中的类型是在匹配子列表之后找到的其他类型。
dropArgumentsToMatch
的结果将具有参数类型列表S..., P..., M..., A...
,其中P
和A
类型将按照dropArguments(MethodHandle, int, Class[])
的方式插入。- API 注释:
-
两个参数列表“实质上相同”(即在共同前缀中相同)的方法句柄可以通过两次调用
dropArgumentsToMatch
相互转换为共同类型,如下所示:import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... ... MethodHandle h0 = constant(boolean.class, true); MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class); MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList()); if (h1.type().parameterCount() < h2.type().parameterCount()) h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0); // lengthen h1 else h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0); // lengthen h2 MethodHandle h3 = guardWithTest(h0, h1, h2); assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
- 参数:
-
target
- 要适配的方法句柄 -
skip
- 要忽略的目标参数数量(它们将保持不变) -
newTypes
- 要匹配target
参数类型列表的类型列表 -
pos
- 在newTypes
中的位置,非跳过的目标参数必须出现的位置 - 返回:
- 可能经过调整的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为空 -
IllegalArgumentException
- 如果newTypes
的任何元素是void.class
,或者skip
为负数或大于目标的参数数量,或者pos
为负数或大于newTypes
列表大小,或者newTypes
不包含target
的非跳过参数类型在位置pos
。 - 自 JDK 版本:
- 9
- 目标句柄具有参数类型列表
-
dropReturn
删除目标句柄的返回值(如果有)。返回的方法句柄将具有void
返回类型。- 参数:
-
target
- 要适配的方法句柄 - 返回:
- 可能经过调整的方法句柄
- 抛出:
-
NullPointerException
- 如果target
为空 - 自 JDK 版本:
- 16
-
filterArguments
通过对一个或多个参数进行预处理,每个参数都有自己的一元过滤函数,然后用每个预处理参数的结果替换目标参数,从而适配目标方法句柄。预处理由filters
数组的元素中指定的一个或多个方法句柄执行。过滤数组的第一个元素对应于目标的pos
参数,以此类推。过滤函数按从左到右的顺序调用。数组中的空参数被视为恒等函数,并且相应的参数保持不变。(如果数组中没有非空元素,则返回原始目标。)每个过滤器都应用于适配器的相应参数。
如果过滤器
F
应用于目标的第N
个参数,则F
必须是一个接受一个参数的方法句柄。F
的唯一参数的类型将替换结果适配器的目标相应参数类型。F
的返回类型必须与目标的相应参数类型相同。如果
filters
中有与目标不对应的元素(无论是否为空),则会出错。示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle upcase = lookup().findVirtual(String.class, "toUpperCase", methodType(String.class)); assertEquals("xy", (String) cat.invokeExact("x", "y")); MethodHandle f0 = filterArguments(cat, 0, upcase); assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy MethodHandle f1 = filterArguments(cat, 1, upcase); assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY MethodHandle f2 = filterArguments(cat, 0, upcase, upcase); assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
以下是生成适配器的伪代码。在代码中,
T
表示target
和生成适配器的返回类型。P
/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...); }
注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。
- 参数:
-
target
- 过滤参数后要调用的方法句柄 -
pos
- 要过滤的第一个参数的位置 -
filters
- 要在过滤后的参数上最初调用的方法句柄 - 返回:
- 包含指定参数过滤逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果目标为null或者filters
数组为null -
IllegalArgumentException
- 如果filters
的非null元素与目标的相应参数类型不匹配,或者pos+filters.length
大于target.type().parameterCount()
,或者结果方法句柄的类型会有太多参数
-
collectArguments
通过使用过滤器(另一个方法句柄)对目标方法句柄进行预处理,使其参数的子序列进行预处理。预处理后的参数将被过滤器函数的结果(如果有的话)替换。然后在修改后的(通常是缩短的)参数列表上调用目标。如果过滤器返回一个值,则目标必须接受该值作为其在位置
pos
的参数,之前和/或之后是未传递给过滤器的任何参数。如果过滤器返回void,则目标必须接受未传递给过滤器的所有参数。不会重新排序参数,并且从过滤器返回的结果将替换(按顺序)最初传递给适配器的参数子序列。过滤器的参数类型(如果有)将替换目标的一个参数类型(在位置
pos
),在生成的适配的方法句柄中。过滤器的返回类型(如果有)必须与目标在位置pos
的参数类型相同,并且该目标参数由过滤器的返回值提供。在所有情况下,
pos
必须大于或等于零,并且pos
也必须小于或等于目标的参数个数。示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle deepToString = publicLookup() .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); MethodHandle ts1 = deepToString.asCollector(String[].class, 1); assertEquals("[strange]", (String) ts1.invokeExact("strange")); MethodHandle ts2 = deepToString.asCollector(String[].class, 2); assertEquals("[up, down]", (String) ts2.invokeExact("up", "down")); MethodHandle ts3 = deepToString.asCollector(String[].class, 3); MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2); assertEquals("[top, [up, down], strange]", (String) ts3_ts2.invokeExact("top", "up", "down", "strange")); MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1); assertEquals("[top, [up, down], [strange]]", (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange")); MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3); assertEquals("[top, [[up, down, strange], charm], bottom]", (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
以下是生成适配器的伪代码。在代码中,
T
代表target
和生成的适配器的返回类型。V
/v
代表filter
的返回类型和值,它们也出现在target
的签名和参数中,除非V
是void
。A
/a
和C
/c
代表在target
的签名中在收集位置pos
之前和之后的参数类型和值。它们也出现在生成的适配器的签名和参数中,它们围绕B
/b
,后者代表filter
的参数类型和参数(如果有的话)。T target(A...,V,C...); V filter(B...); T adapter(A... a,B... b,C... c) { V v = filter(b...); return target(a...,v,c...); } // 如果过滤器没有参数: T target2(A...,V,C...); V filter2(); T adapter2(A... a,C... c) { V v = filter2(); return target2(a...,v,c...); } // 如果过滤器返回void: T target3(A...,C...); void filter3(B...); T adapter3(A... a,B... b,C... c) { filter3(b...); return target3(a...,c...); }
收集适配器
collectArguments(mh, 0, coll)
等同于首先“折叠”受影响的参数,然后在单独的步骤中将它们删除,如下所示:mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //步骤2 mh = MethodHandles.foldArguments(mh, coll); //步骤1
coll
的结果(如果有的话)之外不消耗任何参数,则collectArguments(mh, 0, coll)
等同于filterReturnValue(coll, mh)
。如果过滤器方法句柄coll
消耗一个参数并产生非void结果,则collectArguments(mh, N, coll)
等同于filterArguments(mh, N, coll)
。其他等价关系是可能的,但需要参数排列。注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。
- 参数:
-
target
- 在过滤参数子序列之前要调用的方法句柄 -
pos
- 要传递给过滤器的第一个适配器参数的位置,和/或接收过滤器结果的目标参数的位置 -
filter
- 要在参数子序列上调用的方法句柄 - 返回:
- 包含指定参数子序列过滤逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果filter
的返回类型不是void且与目标的pos
参数不同,或者pos
不在0和目标的参数个数之间(包括)之间,或者结果方法句柄的类型会有太多参数 - 参见:
-
filterReturnValue
通过使用过滤器(另一个方法句柄)对目标方法句柄的返回值(如果有的话)进行后处理,返回过滤器的结果。如果目标返回一个值,则过滤器必须接受该值作为其唯一参数。如果目标返回void,则过滤器不得接受任何参数。
过滤器的返回类型将替换生成的适配方法句柄中目标的返回类型。过滤器的参数类型(如果有)必须与目标的返回类型相同。
示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle length = lookup().findVirtual(String.class, "length", methodType(int.class)); System.out.println((String) cat.invokeExact("x", "y")); // xy MethodHandle f0 = filterReturnValue(cat, length); System.out.println((int) f0.invokeExact("x", "y")); // 2
以下是生成适配器的伪代码。在代码中,
T
/t
代表target
的结果类型和值;V
代表filter
的结果类型;A
/a
代表target
的参数和参数的类型和值,以及生成的适配器。T target(A...); V filter(T); V adapter(A... a) { T t = target(a...); return filter(t); } // 如果目标返回void: void target2(A...); V filter2(); V adapter2(A... a) { target2(a...); return filter2(); } // 如果过滤器返回void: T target3(A...); void filter3(V); void adapter3(A... a) { T t = target3(a...); filter3(t); }
注意: 生成的适配器永远不会是一个可变参数方法句柄,即使原始目标方法句柄是。
- 参数:
-
target
- 在过滤返回值之前要调用的方法句柄 -
filter
- 要在返回值上调用的方法句柄 - 返回:
- 包含指定返回值过滤逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果filter
的参数列表与目标的返回类型不匹配,如上所述
-
foldArguments
通过预处理一些参数来调整目标方法句柄,然后使用预处理的结果调用目标,插入到原始参数序列中。预处理由第二个方法句柄
combiner
执行。传递给适配器的参数中,前N
个参数被复制到combiner,然后调用它。(这里,N
被定义为combiner的参数计数。)之后,控制传递到目标,任何来自combiner的结果都会插入到原始N
个传入参数之前。如果combiner返回一个值,则目标的第一个参数类型必须与combiner的返回类型相同,并且目标的接下来的
N
个参数类型必须与combiner的参数完全匹配。如果combiner返回void,则不会插入任何结果,并且目标的前
N
个参数类型必须与combiner的参数完全匹配。生成的适配器与目标具有相同的类型,除非第一个参数类型与combiner的结果相对应,否则会被删除。
(请注意,
dropArguments
可用于删除combiner或目标不希望接收的任何参数。如果一些传入参数仅用于combiner,请考虑使用asCollector
,因为这些参数在进入目标时不需要保持活动状态。)示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class, "println", methodType(void.class, String.class)) .bindTo(System.out); MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); MethodHandle catTrace = foldArguments(cat, trace); // 也会打印“boo”: assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
这是生成适配器的伪代码。在代码中,
T
表示target
和生成的适配器的结果类型。V
/v
表示target
的参数和参数的类型和值,这些参数和参数位于折叠位置之前;V
也是combiner
的结果类型。A
/a
表示折叠位置处的N
个参数和参数的类型和值。B
/b
表示target
参数和参数的类型和值,这些参数和参数跟随折叠参数和参数。// 在A中有N个参数... T target(V, A[N]..., B...); V combiner(A...); T adapter(A... a, B... b) { V v = combiner(a...); return target(v, a..., b...); } // 如果combiner返回void: T target2(A[N]..., B...); void combiner2(A...); T adapter2(A... a, B... b) { combiner2(a...); return target2(a..., b...); }
注意:生成的适配器永远不会是可变参数方法句柄,即使原始目标方法句柄是。
- 参数:
-
target
- 在组合参数后调用的方法句柄 -
combiner
- 最初在传入参数上调用的方法句柄 - 返回:
- 包含指定参数折叠逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果combiner
的返回类型非void且与目标的第一个参数类型不同,或者如果目标的初始N
个参数类型(跳过与combiner
的返回类型匹配的一个)与combiner
的参数类型不相同
-
foldArguments
通过预处理一些参数来调整目标方法句柄,从给定位置开始,并使用预处理的结果调用目标,插入到折叠参数之前的原始参数序列中。此方法与
foldArguments(MethodHandle, MethodHandle)
密切相关,但允许控制参数列表中折叠发生的位置。控制此位置的参数pos
是从零开始的索引。上述方法foldArguments(MethodHandle, MethodHandle)
假定位置为0。- API注释:
-
示例:
import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; ... MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class, "println", methodType(void.class, String.class)) .bindTo(System.out); MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); MethodHandle catTrace = foldArguments(cat, 1, trace); // 也会打印“jum”: assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
这是生成适配器的伪代码。在代码中,
T
表示三个涉及句柄的统一结果类型;A
/a
表示由test
消耗的target
参数和参数的类型和值;B
/b
表示test
未消耗的target
参数和参数的类型和值。// 在A中有N个参数... T target(Z..., V, A[N]..., B...); V combiner(A...); T adapter(Z... z, A... a, B... b) { V v = combiner(a...); return target(z..., v, a..., b...); } // 如果combiner返回void: T target2(Z..., A[N]..., B...); void combiner2(A...); T adapter2(Z... z, A... a, B... b) { combiner2(a...); return target2(z..., a..., b...); }
注意:生成的适配器永远不会是可变参数方法句柄,即使原始目标方法句柄是。
- 参数:
-
target
- 在组合参数后调用的方法句柄 -
pos
- 开始折叠和插入折叠结果的位置;如果这是0
,效果与foldArguments(MethodHandle, MethodHandle)
相同。 -
combiner
- 最初在传入参数上调用的方法句柄 - 返回:
- 包含指定参数折叠逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果以下两个条件之一成立:(1)combiner
的返回类型为非void
且与目标签名的位置pos
的参数类型不同;(2)目标签名的位置pos
处的N
个参数类型(跳过与combiner
的返回类型匹配的一个)与combiner
的参数类型不相同。 - 自:
- 9
- 参见:
-
guardWithTest
public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) 通过使用测试,一个返回布尔值的方法句柄,保护目标方法句柄来制作一个方法句柄适配器。如果测试失败,则调用一个备用句柄。这三个方法句柄必须具有相同的对应参数和返回类型,除了测试的返回类型必须是布尔值,并且测试允许的参数少于其他两个方法句柄。这是生成适配器的伪代码。在代码中,
T
表示三个涉及句柄的统一结果类型;A
/a
表示由test
消耗的target
参数和参数的类型和值;B
/b
表示test
未消耗的target
参数和参数的类型和值。boolean test(A...); T target(A...,B...); T fallback(A...,B...); T adapter(A... a,B... b) { if (test(a...)) return target(a..., b...); else return fallback(a..., b...); }
a...
)不会被测试的执行修改,因此会根据需要从调用者传递给目标或备用。- 参数:
-
test
- 用于测试的方法句柄,必须返回布尔值 -
target
- 如果测试通过,则调用的方法句柄 -
fallback
- 如果测试失败,则调用的方法句柄 - 返回:
- 包含指定的if/then/else逻辑的方法句柄
- 抛出:
-
NullPointerException
- 如果任何参数为null -
IllegalArgumentException
- 如果test
不返回布尔值,或者三种方法类型不匹配(将test
的返回类型更改为与目标类型匹配)。
-
catchException
public static MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler) 创建一个方法句柄,通过在异常处理程序中运行目标方法句柄来适应它。如果目标正常返回,则适配器返回该值。如果抛出与指定类型匹配的异常,则调用回退句柄处理异常,以及原始参数。目标和处理程序必须具有相同的对应参数和返回类型,除了处理程序可以省略尾随参数(类似于
guardWithTest
中的谓词)。此外,处理程序必须具有额外的前导参数exType
或超类型。以下是生成的适配器的伪代码。在代码中,
T
表示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
不接受给定的异常类型,或者方法句柄类型在返回类型和对应参数方面不匹配 - 参见:
-
throwException
生成一个方法句柄,将抛出给定exType
的异常。该方法句柄将接受一个exType
的单个参数,并立即将其作为异常抛出。方法类型将名义上指定为returnType
的返回。返回类型可以是任何方便的类型:对于方法句柄的行为来说并不重要,因为它永远不会正常返回。- 参数:
-
returnType
- 所需方法句柄的返回类型 -
exType
- 所需方法句柄的参数类型 - 返回:
- 可以抛出给定异常的方法句柄
- 抛出:
-
NullPointerException
- 如果任一参数为null
-
loop
构造一个方法句柄,表示一个循环,其中有几个循环变量在每次迭代时更新和检查。当循环由于其中一个谓词而终止时,将运行相应的终结器并传递循环的结果,这是生成句柄的返回值。直观地说,每个循环由一个或多个“子句”组成,每个子句指定一个本地迭代变量和/或一个循环退出。循环的每次迭代按顺序执行每个子句。子句可以选择更新其迭代变量;它还可以选择执行测试和条件循环退出。为了用方法句柄来表达这种逻辑,每个子句将指定最多四个独立的操作:
- init:在循环执行之前,初始化类型为
V
的迭代变量v
。 - step:当子句执行时,更新迭代变量
v
的步骤。 - pred:当子句执行时,执行谓词以测试循环退出。
- fini:如果子句导致循环退出,则执行终结器以计算循环的返回值。
(V...)
。这些值本身将是(v...)
。当我们谈到“参数列表”时,我们通常指的是类型,但在某些上下文中(描述执行时),列表将是实际值。根据某些规则,可以省略其中一些子句部分,并在这种情况下提供有用的默认行为。详细描述请参见下文。
任何地方都是可选的参数:每个子句函数都允许但不是必须接受每个迭代变量
v
的参数。作为一个例外,init函数不能接受任何v
参数,因为在执行init函数时尚未计算这些值。任何子句函数都可以忽略接受其有权接受的任何尾随子序列的参数。实际上,任何子句函数都可以不接受任何参数。循环参数:子句函数可以接受其有权接受的所有迭代变量值,这种情况下它也可以接受更多的尾随参数。这些额外的值称为循环参数,其类型和值标记为
(A...)
和(a...)
。这些将成为生成的循环句柄的参数,在执行循环时提供。 (由于init函数不接受迭代变量v
,因此传递给init函数的任何参数都自动成为循环参数a
。)与迭代变量一样,子句函数允许但不是必须接受循环参数。这些循环参数充当整个循环中可见的循环不变值。任何地方都可见的参数:每个非init子句函数都允许观察整个循环状态,因为它可以传递当前迭代变量值和传入循环参数的完整列表
(v... a...)
。init函数可以观察初始的前循环状态,形式为(a...)
。大多数子句函数不需要所有这些信息,但它们将被正式连接到这些信息,就好像通过dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>)
一样。 更具体地说,我们将使用符号(V*)
来表示完整序列(V...)
的任意前缀(对于(v*)
、(A*)
和(a*)
同样如此)。在该符号中,init函数参数列表的一般形式是(A*)
,非init函数参数列表的一般形式是(V*)
或(V... A*)
。检查子句结构:给定一组子句,将执行一系列检查和调整以连接循环的所有部分。这些步骤在下面详细说明。在这些步骤中,每次出现“必须”一词时,如果循环组合器的输入不满足所需约束,则将抛出
IllegalArgumentException
。有效相同的序列: 定义参数列表
A
与另一个参数列表B
是有效相同的,如果A
和B
是相同的,或者如果A
更短且与B
的一个适当前缀相同。当谈到无序的参数列表集时,我们说整个集合作为一个整体是“有效相同”的,如果集合包含一个最长列表,并且集合中的所有成员都与该最长列表有效相同。例如,任何形式为(V*)
的类型序列集合是有效相同的,如果添加更多形式为(V... A*)
的序列,情况也是如此。步骤0:确定子句结构。
- 子句数组(类型为
MethodHandle[][]
)必须为非null
,并且至少包含一个元素。 - 子句数组不得包含
null
或超过四个元素的子数组。 - 长度小于四个元素的子句将被视为通过附加元素到数组中进行填充。
- 所有
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函数的参数列表将调整为外部参数列表。(注意,它们的参数列表已经有效相同于此列表。)
- 每个非省略的非init(step、pred和fini)函数的参数列表必须有效相同于内部参数列表
(V... A...)
。
步骤3:填充省略的函数。
- 如果省略了init函数,则对应子句的迭代变量类型使用默认值。
- 如果省略了step函数,则使用子句的迭代变量类型的恒等函数;在前面子句的非
void
迭代变量的恒等函数参数之前插入被丢弃的参数。(这将使循环变量成为本地循环不变量。) - 如果省略了pred函数,则使用一个常量
true
函数。(这将使循环继续进行,就该子句而言。请注意,在这种情况下,相应的fini函数是无法到达的。) - 如果省略了fini函数,则使用默认值作为循环返回类型。
步骤4:填充缺失的参数类型。
- 此时,每个init函数的参数列表都有效相同于外部参数列表
(A...)
,但某些列表可能较短。对于参数列表较短的每个init函数,填充列表末尾。 - 此时,每个非init函数的参数列表都有效相同于内部参数列表
(V... A...)
,但某些列表可能较短。对于参数列表较短的每个非init函数,填充列表末尾。 - 参数列表通过删除未使用的尾随参数进行填充。
最终观察。
- 经过这些步骤,所有子句都已通过提供省略的函数和参数进行调整。
- 所有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
是非void类型Rn
的计数,(V1...VQ)
是这些类型的序列。 - 必须满足
Vn == Bn
,对于n = 1..min(X,Q)
。 - 参数类型
Vn
将被解释为循环局部状态元素(V...)
。 - 任何剩余的类型
BQ+1..BX
(如果Q
)将确定生成的循环处理器的参数类型(A...)
。
(A...)
是从步骤函数派生的,如果大部分循环计算发生在步骤中,这是很自然的。对于一些循环,计算的负担可能在谓词函数中最重,因此谓词函数可能需要接受循环参数值。对于具有复杂退出逻辑的循环,结束函数可能需要接受循环参数,同样对于具有复杂进入逻辑的循环,初始化函数将需要额外的参数。出于这些原因,确定这些参数的规则尽可能对称,跨所有子句部分。一般来说,循环参数作为整个循环中的共同不变值,而迭代变量作为共同变化值,或者(如果没有步骤函数)作为内部循环不变临时变量。循环执行。
- 当循环被调用时,循环输入值被保存在本地变量中,以便传递给每个子句函数。这些本地变量是循环不变的。
- 每个初始化函数按子句顺序执行(传递外部参数
(a...)
),非void
值被保存(作为迭代变量(v...)
)到本地变量中。这些本地变量将是循环变化的(除非它们的步骤行为像恒等函数一样,如上所述)。 - 所有函数执行(除了初始化函数)将传递内部参数列表,包括非
void
迭代值(v...)
(按子句顺序)然后是循环输入(a...)
(按参数顺序)。 - 然后执行步骤和谓词函数,按子句顺序执行(步骤在谓词之前),直到谓词函数返回
false
。 - 来自步骤函数调用的非
void
结果用于更新循环变量序列(v...)
中相应的值。更新后的值立即对所有后续函数调用可见。 - 如果谓词函数返回
false
,则调用相应的结束函数,并将结果值(类型为R
)作为整个循环的返回值。 - 如果所有谓词函数始终返回true,则永远不会调用任何结束函数,并且循环除非抛出异常否则无法退出。
使用提示。
- 虽然每个步骤函数将接收所有循环变量的当前值,但有时步骤函数只需要观察自己变量的当前值。在这种情况下,步骤函数可能需要显式地丢弃所有前面的循环变量。这将需要提及它们的类型,如
dropArguments(step, 0, V0.class, ...)
这样的表达式。 - 循环变量不需要变化;它们可以是循环不变的。一个子句可以通过适当的初始化函数创建一个循环不变量,该函数没有步骤、谓词或结束函数。这可能对将传入的循环参数"连接"到相邻循环变量的步骤或谓词函数中是有用的。
- 如果一些子句函数是实例上的虚拟方法,实例本身可以方便地放置在初始不变循环"变量"中,使用类似
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注释:
-
示例:
// 阶乘函数的迭代实现作为循环句柄 static int one(int k) { return 1; } static int inc(int i, int acc, int k) { return i + 1; } static int mult(int i, int acc, int k) { return i * acc; } static boolean pred(int i, int acc, int k) { return i < k; } static int fin(int i, int acc, int k) { return acc; } // 假设MH_one、MH_inc、MH_mult、MH_pred和MH_fin是上述方法的句柄 // 计数器的空初始化器,应初始化为0 MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); assertEquals(120, loop.invoke(5));
// 阶乘函数的简化实现作为循环句柄 static int inc(int i) { return i + 1; } // 省略acc, k static int mult(int i, int acc) { return i * acc; } //省略k static boolean cmp(int i, int k) { return i < k; } // 假设MH_inc、MH_mult和MH_cmp是上述方法的句柄 // 计数器的空初始化器,应初始化为0 MethodHandle MH_one = MethodHandles.constant(int.class, 1); MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // 省略acc MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // 省略i MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); assertEquals(720, loop.invoke(6));
// 阶乘函数的基于实例的实现作为循环句柄 static class FacLoop { final int k; FacLoop(int k) { this.k = k; } int inc(int i) { return i + 1; } int mult(int i, int acc) { return i * acc; } boolean pred(int i) { return i < k; } int fin(int i, int acc) { return acc; } } // 假设MH_FacLoop是构造函数的句柄 // 假设MH_inc、MH_mult、MH_pred和MH_fin是上述方法的句柄 // 计数器的空初始化器,应初始化为0 MethodHandle MH_one = MethodHandles.constant(int.class, 1); MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop}; MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause); assertEquals(5040, loop.invoke(7));
- 参数:
-
clauses
- 符合上述规则的MethodHandle
数组的数组(4元组)。 - 返回:
- 体现由参数定义的循环行为的方法句柄。
- 抛出:
-
IllegalArgumentException
- 如果违反上述任何约束。 - 自:
- 9
- 参见:
- init:在循环执行之前,初始化类型为
-
whileLoop
构造一个while
循环,包括初始化器、主体和谓词。这是对通用循环组合器的便利包装。pred
句柄描述了循环条件;body
描述了循环主体。通过此方法得到的循环将在每次迭代中首先评估谓词,然后执行其主体(如果谓词评估为true
)。一旦谓词评估为false
(在这种情况下不会执行主体),循环将终止。init
句柄描述了一个额外可选的循环局部变量的初始值。在每次迭代中,如果存在此循环局部变量,它将被传递给body
并更新为其调用返回的值。循环执行的结果将是额外循环局部变量(如果存在)的最终值。这些参数句柄的规则如下:
body
句柄不能为null
;其类型必须为(V A...)V
的形式,其中V
为非void
,否则为(A...)void
。(在void
情况下,我们将void
类型V
分配给名称V
,并且我们将以理解一个void
类型V
从参数列表中被静默删除的方式写(V A...)V
,留下(A...)V
。)- 主体的参数列表
(V A...)
称为内部参数列表。它将约束其他循环部分的参数列表。 - 如果迭代变量类型
V
从内部参数列表中删除,则产生的较短列表(A...)
称为外部参数列表。 - 如果主体返回类型
V
为非void
,则确定循环的一个额外状态变量的类型V
。主体必须同时接受并返回此类型V
的值。 - 如果
init
不为null
,则必须具有返回类型V
。其参数列表(某种(A*)
形式)必须与外部参数列表(A...)
有效地相同。 - 如果
init
为null
,则循环变量将被初始化为其默认值
。 pred
句柄不能为null
。其返回类型必须为boolean
。其参数列表(空或(V A*)
形式)必须与内部参数列表有效地相同。
生成的循环句柄的结果类型和参数签名如下确定:
- 循环句柄的结果类型是主体的结果类型
V
。 - 循环句柄的参数类型是外部参数列表
(A...)
的类型。
以下是生成的循环句柄的伪代码。在代码中,
V
/v
表示唯一循环变量的类型/值以及循环的结果类型;A
/a
表示传递给循环的参数的类型/值。V init(A...); boolean pred(V, A...); V body(V, A...); V whileLoop(A... a...) { V v = init(a...); while (pred(v, a...)) { v = body(v, a...); } return v; }
- API注释:
-
示例:
// 作为循环句柄实现列表的zip函数 static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); } static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); } static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) { zip.add(a.next()); zip.add(b.next()); return zip; } // 假设MH_initZip、MH_zipPred和MH_zipStep是上述方法的句柄 MethodHandle loop = MethodHandles.whileLoop(MH_initZip, MH_zipPred, MH_zipStep); List<String> a = Arrays.asList("a", "b", "c", "d"); List<String> b = Arrays.asList("e", "f", "g", "h"); List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h"); assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { MethodHandle fini = (body.type().returnType() == void.class ? null : identity(body.type().returnType())); MethodHandle[] checkExit = { null, null, pred, fini }, varBody = { init, body }; return loop(checkExit, varBody); }
- 参数:
-
init
- 可选的初始化器,提供循环变量的初始值。可以为null
,暗示默认初始值。其他约束请参见上文。 -
pred
- 循环条件,不得为null
。其结果类型必须为boolean
。其他约束请参见上文。 -
body
- 循环主体,不得为null
。它控制循环的参数和结果类型。其他约束请参见上文。 - 返回:
-
实现描述参数所描述的
while
循环的方法句柄。 - 异常:
-
IllegalArgumentException
- 如果违反参数规则。 -
NullPointerException
- 如果pred
或body
为null
。 - 自 JDK 9 起:
- 9
- 参见:
-
doWhileLoop
构造一个do-while
循环,包括初始化器、主体和谓词。这是对通用循环组合器的便利包装。pred
句柄描述了循环条件;body
描述了循环主体。通过此方法得到的循环将在每次迭代中首先执行其主体,然后评估谓词。在执行主体后,一旦谓词评估为false
,循环将终止。init
句柄描述了一个额外可选的循环局部变量的初始值。在每次迭代中,如果存在此循环局部变量,它将被传递给body
并更新为其调用返回的值。循环执行的结果将是额外循环局部变量(如果存在)的最终值。这些参数句柄的规则如下:
body
句柄不能为null
;其类型必须为(V A...)V
的形式,其中V
为非void
,否则为(A...)void
。(在void
情况下,我们将void
类型V
分配给名称V
,并且我们将以理解一个void
类型V
从参数列表中被静默删除的方式写(V A...)V
,留下(A...)V
。)- 主体的参数列表
(V A...)
称为内部参数列表。它将约束其他循环部分的参数列表。 - 如果迭代变量类型
V
从内部参数列表中删除,则产生的较短列表(A...)
称为外部参数列表。 - 主体返回类型
V
,如果非void
,则确定循环的一个额外状态变量的类型V
。主体必须同时接受并返回此类型V
的值。 - 如果
init
不为null
,则必须具有返回类型V
。其参数列表(某种(A*)
形式)必须与外部参数列表(A...)
有效地相同。 - 如果
init
为null
,则循环变量将被初始化为其默认值
。 pred
句柄不能为null
。其返回类型必须为boolean
。其参数列表(空或(V A*)
形式)必须与内部参数列表有效地相同。
生成的循环句柄的结果类型和参数签名如下确定:
- 循环句柄的结果类型是主体的结果类型
V
。 - 循环句柄的参数类型是外部参数列表
(A...)
的类型。
以下是生成的循环句柄的伪代码。在代码中,
V
/v
表示唯一循环变量的类型/值以及循环的结果类型;A
/a
表示传递给循环的参数的类型/值。V init(A...); boolean pred(V, A...); V body(V, A...); V doWhileLoop(A... a...) { V v = init(a...); do { v = body(v, a...); } while (pred(v, a...)); return v; }
- API注释:
-
示例:
// int i = 0; while (i < limit) { ++i; } return i; => limit static int zero(int limit) { return 0; } static int step(int i, int limit) { return i + 1; } static boolean pred(int i, int limit) { return i < limit; } // 假设MH_zero、MH_step和MH_pred是上述方法的句柄 MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred); assertEquals(23, loop.invoke(23));
MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { MethodHandle fini = (body.type().returnType() == void.class ? null : identity(body.type().returnType())); MethodHandle[] clause = { init, body, pred, fini }; return loop(clause); }
- 参数:
-
init
- 可选的初始化器,提供循环变量的初始值。可以为null
,表示默认初始值。其他约束请参见上文。 -
body
- 循环体,不可为null
。它控制循环的参数和结果类型。其他约束请参见上文。 -
pred
- 循环条件,不可为null
。其结果类型必须为boolean
。其他约束请参见上文。 - 返回值:
-
实现描述的
while
循环的方法句柄。 - 抛出:
-
IllegalArgumentException
- 如果违反参数规则。 -
NullPointerException
- 如果pred
或body
为null
。 - 自版本:
- 9
- 参见:
-
countedLoop
public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) 构造一个运行给定迭代次数的循环。这是通用循环组合器的便利包装器。迭代次数由
iterations
句柄的评估结果确定。循环计数器i
是一个额外的循环迭代变量,类型为int
。它将被初始化为0,并在每次迭代中递增1。如果
body
句柄返回非void
类型V
,则还会存在一个该类型的额外循环迭代变量。此变量将使用可选的init
句柄进行初始化,或者如果该句柄为null
,则使用类型V
的默认值进行初始化。在每次迭代中,迭代变量将传递给
body
句柄的调用。从循环体返回的非void
值(类型为V
)将更新额外的迭代变量。循环句柄执行的结果将是该变量的最终V
值(如果没有V
变量,则为void
)。以下规则适用于参数句柄:
iterations
句柄不能为null
,必须返回类型为int
,在参数类型列表中称为I
。body
句柄不能为null
;其类型必须为(V I A...)V
的形式,其中V
为非void
,否则为(I A...)void
。(在void
情况下,我们将V
类型为void
,并且我们将(V I A...)V
写为理解为void
类型V
在参数列表中被静默删除,留下(I A...)V
。)- 循环体的参数列表
(V I A...)
贡献给一个称为内部参数列表的类型列表。它将约束其他循环部分的参数列表。 - 作为特例,如果循环体仅贡献
V
和I
类型,没有额外的A
类型,则内部参数列表将扩展为iterations
句柄的参数类型A...
。 - 如果迭代变量类型
(V I)
从内部参数列表中删除,则产生的较短列表(A...)
称为外部参数列表。 - 循环体的返回类型
V
,如果非void
,将确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回该类型V
的值。 - 如果
init
不为null
,则其必须具有返回类型V
。其参数列表(某种形式为(A*)
)必须有效相同于外部参数列表(A...)
。 - 如果
init
为null
,则循环变量将被初始化为其默认值。 iterations
的参数列表(某种形式为(A*)
)必须有效相同于外部参数列表(A...)
。
生成的循环句柄的结果类型和参数签名确定如下:
- 循环句柄的结果类型是循环体的结果类型
V
。 - 循环句柄的参数类型是外部参数列表
(A...)
中的类型。
以下是生成的循环句柄的伪代码。在代码中,
V
/v
表示第二个循环变量的类型/值以及循环的结果类型;A...
/a...
表示传递给循环的参数。int iterations(A...); V init(A...); V body(V, int, A...); V countedLoop(A... a...) { int end = iterations(a...); V v = init(a...); for (int i = 0; i < end; ++i) { v = body(v, i, a...); } return v; }
- API注释:
-
具有完全符合体方法的示例:
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; // => 一个众所周知的主题的变体 static String step(String v, int counter, String init) { return "na " + v; } // 假设MH_step是上述方法的句柄 MethodHandle fit13 = MethodHandles.constant(int.class, 13); MethodHandle start = MethodHandles.identity(String.class); MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step); assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; // => 一个众所周知的主题的变体 static String step(String v, int counter ) { return "na " + v; } // 假设MH_step是上述方法的句柄 MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class); MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class); MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i) -> "na " + v assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s; // => 一个众所周知的主题的变体 static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } // 假设MH_step是上述方法的句柄 MethodHandle count = MethodHandles.identity(int.class); MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class); MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i, _, pre, _) -> pre + " " + v assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
dropArgumentsToMatch(MethodHandle, int, List, int)
来强制执行循环类型的示例:// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s; // => 一个众所周知的主题的变体 static String step(String v, int counter, String pre) { return pre + " " + v; } // 假设MH_step是上述方法的句柄 MethodType loopType = methodType(String.class, String.class, int.class, String.class); MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class), 0, loopType.parameterList(), 1); MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2); MethodHandle body = MethodHandles.dropArgumentsToMatch(MH_step, 2, loopType.parameterList(), 0); MethodHandle loop = MethodHandles.countedLoop(count, start, body); // (v, i, pre, _, _) -> pre + " " + v assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { return countedLoop(empty(iterations.type()), iterations, init, body); }
- 参数:
-
iterations
- 用于返回此循环应运行的迭代次数的非null
句柄。句柄的结果类型必须为int
。有关其他约束,请参见上文。 -
init
- 可选的初始化器,提供循环变量的初始值。可以为null
,表示默认初始值。有关其他约束,请参见上文。 -
body
- 循环体,不得为null
。它控制标准情况下的循环参数和结果类型。它必须接受自己的返回类型(如果非void)加上一个int
参数(用于计数),并且可以接受任意数量的其他类型。有关其他约束,请参见上文。 - 返回:
- 代表循环的方法句柄。
- 抛出:
-
NullPointerException
- 如果iterations
或body
句柄中的任一句柄为null
。 -
IllegalArgumentException
- 如果任何参数违反上述规则。 - 自:
- 9
- 参见:
-
countedLoop
public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) 构造一个在一系列数字上计数的循环。这是通用循环组合器的便利包装器。循环计数器
i
是一个类型为int
的循环迭代变量。start
和end
句柄确定循环计数器的起始(包括)和结束(不包括)值。循环计数器将从start
句柄的评估返回的int
值初始化,并以从end
返回的值(不包括)以步宽1运行。如果
body
句柄返回非void
类型V
,则该类型的一个前导循环迭代变量也存在。此变量使用可选的init
句柄初始化,或者如果该句柄为null
,则使用类型V
的默认值初始化。在每次迭代中,迭代变量将传递给
body
句柄的调用。从循环体返回的非void
值(类型为V
)将更新前导迭代变量。循环句柄执行的结果将是该变量的最终V
值(如果没有V
变量,则为void
)。以下规则适用于参数句柄:
start
和end
句柄不能为null
,并且必须都返回公共类型int
,在参数类型列表中称为I
。body
句柄不能为null
;其类型必须为(V I A...)V
的形式,其中V
为非void
,否则为(I A...)void
。(在void
情况下,我们将void
类型V
分配给名称V
,并且我们将(V I A...)V
写为理解到void
类型V
已从参数列表中静默删除,留下(I A...)V
。)- 循环体的参数列表
(V I A...)
贡献给称为内部参数列表的类型列表。它将约束其他循环部分的参数列表。 - 作为特例,如果循环体仅贡献
V
和I
类型,没有额外的A
类型,则内部参数列表将由end
句柄的参数类型A...
扩展。 - 如果迭代变量类型
(V I)
从内部参数列表中删除,则结果较短的列表(A...)
称为外部参数列表。 - 循环体的返回类型
V
(如果非void
)确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回此类型V
的值。 - 如果
init
为非null
,则其必须具有返回类型V
。其参数列表(某种形式(A*)
)必须有效相同于外部参数列表(A...)
。 - 如果
init
为null
,则循环变量将初始化为其默认值。 start
的参数列表(某种形式(A*)
)必须有效相同于外部参数列表(A...)
。- 同样,
end
的参数列表必须有效相同于外部参数列表。
生成的循环句柄的结果类型和参数签名如下确定:
- 循环句柄的结果类型是循环体的结果类型
V
。 - 循环句柄的参数类型是外部参数列表
(A...)
中的类型。
以下是生成的循环句柄的伪代码。在代码中,
V
/v
表示第二个循环变量的类型/值以及循环的结果类型;A...
/a...
表示传递给循环的参数。int start(A...); int end(A...); V init(A...); V body(V, int, A...); V countedLoop(A... a...) { int e = end(a...); int s = start(a...); V v = init(a...); for (int i = s; i < e; ++i) { v = body(v, i, a...); } return v; }
- API注释:
-
此方法的实现可以表达如下:
MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class); // 假设MH_increment和MH_predicate是指向具有以下语义的内部实现方法的句柄: // MH_increment: (int limit, int counter) -> counter + 1 // MH_predicate: (int limit, int counter) -> counter < limit Class<?> counterType = start.type().returnType(); // int Class<?> returnType = body.type().returnType(); MethodHandle incr = MH_increment, pred = MH_predicate, retv = null; if (returnType != void.class) { // 忽略V变量 incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i) pred = dropArguments(pred, 1, returnType); // 同上 retv = dropArguments(identity(returnType), 0, counterType); // 忽略limit } body = dropArguments(body, 0, counterType); // 忽略limit变量 MethodHandle[] loopLimit = { end, null, pred, retv }, // limit = end(); i < limit || return v bodyClause = { init, body }, // v = init(); v = body(v, i) indexVar = { start, incr }; // i = start(); i = i + 1 return loop(loopLimit, bodyClause, indexVar); }
- 参数:
-
start
- 用于返回循环计数器起始值的非null
句柄,必须为int
类型。其他约束请参见上文。 -
end
- 用于返回循环计数器结束值的非null
句柄(循环将运行到end-1
)。结果类型必须为int
。其他约束请参见上文。 -
init
- 可选的初始化器,提供循环变量的初始值。可以为null
,表示默认初始值。其他约束请参见上文。 -
body
- 循环体,不得为null
。在标准情况下,它控制循环参数和结果类型(详细信息请参见上文)。如果返回类型为非void,则必须接受自身的返回类型(如果非void)加上一个int
参数(用于计数器),并且可以接受任意数量的其他类型。其他约束请参见上文。 - 返回:
- 代表循环的方法句柄。
- 抛出:
-
NullPointerException
- 如果start
、end
或body
句柄中有任何一个为null
。 -
IllegalArgumentException
- 如果任何参数违反上述规则。 - 自版本:
- 9
- 参见:
-
iteratedLoop
public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) 构造一个循环,范围为由Iterator<T>
生成的值。这是通用循环组合器的便捷包装器。迭代器本身将由
iterator
句柄的评估确定。它产生的每个值将存储在类型为T
的循环迭代变量中。如果
body
句柄返回非void
类型V
,则还将存在一个前导循环迭代变量。此变量将使用可选的init
句柄进行初始化,或者如果该句柄为null
,则使用类型V
的默认值。在每次迭代中,迭代变量将传递给
body
句柄的调用。从body
返回的非void
值(类型为V
)将更新前导迭代变量。循环句柄执行的结果将是该变量的最终V
值(如果没有V
变量,则为void
)。以下规则适用于参数句柄:
body
句柄不得为null
;其类型必须为(V T A...)V
形式,其中V
为非void
,否则为(T A...)void
。(在void
情况下,我们将类型void
分配给名称V
,并且我们将在理解中写(V T A...)V
,理解为void
类型V
在参数列表中被静默删除,留下(T A...)V
。)- 循环体的参数列表
(V T A...)
将贡献到称为内部参数列表的类型列表中。它将约束其他循环部分的参数列表。 - 作为特例,如果循环体仅贡献
V
和T
类型,没有额外的A
类型,则内部参数列表将扩展为iterator
句柄的A...
参数类型;如果它为null
,则添加单个类型Iterable
并构成A...
列表。 - 如果迭代变量类型
(V T)
从内部参数列表中删除,则生成的较短列表(A...)
称为外部参数列表。 - 循环体返回类型
V
(如果非void
)确定循环的附加状态变量的类型。循环体必须同时接受一个前导参数并返回此类型V
的值。 - 如果
init
为非null
,则其必须具有返回类型V
。其参数列表(某种形式为(A*)
)必须有效地相同于外部参数列表(A...)
。 - 如果
init
为null
,则循环变量将初始化为其默认值。 - 如果
iterator
句柄为非null
,则其必须具有返回类型java.util.Iterator
或其子类型。在执行循环时,假定迭代器产生的值可以转换为类型T
。 - 非
null
的iterator
的参数列表(某种形式为(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...); // 默认为Iterable::iterator V init(A...); V body(V,T,A...); V iteratedLoop(A... a...) { Iterator<T> it = iterator(a...); V v = init(a...); while (it.hasNext()) { T t = it.next(); v = body(v, t, a...); } return v; }
- API注释:
-
示例:
// 从列表中获取迭代器 static List<String> reverseStep(List<String> r, String e) { r.add(0, e); return r; } static List<String> newArrayList() { return new ArrayList<>(); } // 假设MH_reverseStep和MH_newArrayList是上述方法的句柄 MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep); List<String> list = Arrays.asList("a", "b", "c", "d", "e"); List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a"); assertEquals(reversedList, (List<String>) loop.invoke(list));
MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) { // 假设MH_next、MH_hasNext、MH_startIter是Iterator/Iterable方法的句柄 Class<?> returnType = body.type().returnType(); Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1); MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype)); MethodHandle retv = null, step = body, startIter = iterator; if (returnType != void.class) { // 首先简单处理: 在 (I V A...) 中,去掉 I 得到 V retv = dropArguments(identity(returnType), 0, Iterator.class); // body类型签名 (V T A...),内部循环类型 (I V A...) step = swapArguments(body, 0, 1); // 交换 V <-> T } if (startIter == null) startIter = MH_getIter; MethodHandle[] iterVar = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext()) bodyClause = { init, filterArguments(step, 0, nextVal) }; // v = body(v, t, a) return loop(iterVar, bodyClause); }
- 参数:
-
iterator
- 一个可选的句柄,用于返回开始循环的迭代器。如果非null
,则句柄必须返回Iterator
或其子类型。其他约束请参见上文。 -
init
- 可选的初始化器,提供循环变量的初始值。可以为null
,表示默认初始值。其他约束请参见上文。 -
body
- 循环体,不得为null
。在标准情况下,它控制循环参数和结果类型(详细信息请参见上文)。它必须接受自身的返回类型(如果非void)加上一个T
参数(用于迭代值),并且可以接受任意数量的其他类型。其他约束请参见上文。 - 返回:
- 体现迭代循环功能的方法句柄。
- 异常:
-
NullPointerException
- 如果body
句柄为null
。 -
IllegalArgumentException
- 如果任何参数违反上述要求。 - 自 JDK 版本:
- 9
-
tryFinally
创建一个方法句柄,通过将其包装在try-finally
块中来适应target
方法句柄。另一个方法句柄cleanup
代表finally
块的功能。在执行target
句柄期间抛出的任何异常都将传递给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 = (V的零值); Throwable throwable = null; try { result = target(a..., b...); } catch (Throwable t) { throwable = t; throw t; } finally { result = cleanup(throwable, result, a...); } return result; }
请注意,保存的参数(伪代码中的
a...
)不能被目标的执行修改,因此如果调用了清理句柄,则将其无修改地传递给清理句柄。目标和清理必须返回相同的类型,即使清理总是抛出异常。要创建这样一个抛出异常的清理,将清理逻辑与
throwException
组合,以创建具有正确返回类型的方法句柄。请注意,
tryFinally
永远不会将异常转换为正常返回。在极少数情况下,必须以这种方式转换异常时,首先使用catchException(MethodHandle, Class, MethodHandle)
包装目标以捕获传出异常,然后再包装为tryFinally
。建议
cleanup
的第一个参数类型声明为Throwable
而不是更窄的子类型。这确保cleanup
将始终使用target
抛出的任何异常调用。声明更窄的类型可能导致try-finally
句柄抛出ClassCastException
,如果target
抛出的异常类型不能分配给cleanup
的第一个参数类型。请注意,VirtualMachineError
、LinkageError
和RuntimeException
的各种异常类型原则上可以由几乎任何类型的Java代码抛出,而仅捕获(例如)IOException
的finally子句将在ClassCastException
后面掩盖任何其他异常。- 参数:
-
target
- 要包装在try
块中的执行句柄。 -
cleanup
- 在finally块中调用的句柄。 - 返回:
-
体现由这两个参数组成的
try-finally
块的方法句柄。 - 异常:
-
NullPointerException
- 如果任何参数为null -
IllegalArgumentException
- 如果cleanup
不接受所需的前导参数,或者如果方法句柄类型在返回类型和对应的尾随参数上不匹配 - 自 JDK 版本:
- 9
- 参见:
- 一个
-
tableSwitch
创建一个表切换方法句柄,可用于根据给定的目标索引(称为选择器)切换一组目标方法句柄。对于选择器值为
n
,其中n
在范围[0, N)
内,且N
为目标方法句柄的数量,表切换方法句柄将从目标方法句柄列表中调用第n个目标方法句柄。对于不在范围
[0, N)
内的选择器值,表切换方法句柄将调用给定的回退方法句柄。传递给此方法的所有方法句柄必须具有相同的类型,另外要求前导参数为
int
类型。前导参数表示选择器。类型中存在的任何尾随参数也将出现在返回的表切换方法句柄上。将分配给这些参数的任何参数将与选择器值一起转发到调用选定方法句柄时。
- API注释:
-
示例: 每个案例都会丢弃它们所给定的
selector
值,并接受一个额外的String
参数,该参数被连接(使用String.concat(String)
)到每个案例的特定常量标签字符串上:MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle caseMh = lookup.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class)); caseMh = MethodHandles.dropArguments(caseMh, 0, int.class); MethodHandle caseDefault = MethodHandles.insertArguments(caseMh, 1, "default: "); MethodHandle case0 = MethodHandles.insertArguments(caseMh, 1, "case 0: "); MethodHandle case1 = MethodHandles.insertArguments(caseMh, 1, "case 1: "); MethodHandle mhSwitch = MethodHandles.tableSwitch( caseDefault, case0, case1 ); assertEquals("default: data", (String) mhSwitch.invokeExact(-1, "data")); assertEquals("case 0: data", (String) mhSwitch.invokeExact(0, "data")); assertEquals("case 1: data", (String) mhSwitch.invokeExact(1, "data")); assertEquals("default: data", (String) mhSwitch.invokeExact(2, "data"));
- 参数:
-
fallback
- 当选择器不在范围[0, N)
内时调用的回退方法句柄。 -
targets
- 目标方法句柄数组。 - 返回:
- 表切换方法句柄。
- 抛出:
-
NullPointerException
- 如果fallback
、targets
数组或targets
数组的任何元素为null
。 -
IllegalArgumentException
- 如果targets
数组为空,如果回退句柄或任何目标句柄的前导参数不是int
,或者如果回退句柄和所有目标句柄的类型不相同。
-
memorySegmentViewVarHandle
memorySegmentViewVarHandle
是Java平台的预览API。预览功能可能会在将来的版本中被移除,或升级为Java平台的永久功能。创建一个变量句柄对象,可用于根据提供的值布局对给定字节偏移量处的memory segment
预览进行解引用。提供的布局指定了与返回的变量句柄相关联的载体类型预览、字节大小预览、字节对齐预览和字节顺序预览。
与返回的变量句柄相关联的坐标类型列表为
(MemorySegment, long)
,其中long
坐标类型对应于给定内存段坐标中的字节偏移量。因此,返回的变量句柄访问给定内存段中偏移量处的字节,将字节组合到变量句柄类型的值中,或从变量句柄类型的值中提取字节。此外,访问操作将遵守提供的布局中表达的字节顺序和对齐约束。例如,考虑由以下方式构造的
GroupLayout
预览实例表示的内存布局:GroupLayout seq = java.lang.foreign.MemoryLayout.structLayout( MemoryLayout.paddingLayout(4), ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value") );
value
的成员布局,我们可以构造一个内存段视图变量句柄,如下所示:VarHandle handle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); //(MemorySegment, long) -> int handle = MethodHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
- API注释:
-
结果变量句柄具有特定的访问模式限制,这些限制适用于所有内存段视图变量句柄。内存段视图变量句柄关联有一个访问大小
S
和一个对齐约束B
(均以字节表示)。我们说一个内存访问操作是完全对齐的,如果它发生在一个与对齐约束S
和B
都兼容的内存地址A
上。如果访问是完全对齐的,则支持以下访问模式,并且保证支持原子访问:- 对于所有
T
,读写访问模式都受支持,但在32位平台上,long
和double
的get
和set
访问模式除外。 int
、long
、float
、double
或MemorySegment
预览的原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他类型的访问模式。)int
、long
和MemorySegment
预览的数值原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他数值类型的访问模式。)int
、long
和MemorySegment
预览的位原子更新访问模式。(未来JDK的主要平台版本可能支持某些当前不支持的其他数值类型的访问模式。)
T
是float
、double
或MemorySegment
预览,则原子更新访问模式将使用它们的位表示进行值比较(参见Float.floatToRawIntBits(float)
、Double.doubleToRawLongBits(double)
和MemorySegment.address()
预览)。另外,如果内存访问操作是部分对齐的,即发生在仅与对齐约束
B
兼容的内存地址A
上;在这种情况下,除了get
和set
访问模式之外的任何访问都将导致IllegalStateException
。如果访问是部分对齐的,则原子访问仅保证相对于A
和S
的最大二的幂的GCD。在所有其他情况下,我们说内存访问操作是不对齐的;在这种情况下,无论使用的访问模式如何,都会抛出
IllegalStateException
。最后,如果
T
是MemorySegment
,则所有写访问模式都会抛出IllegalArgumentException
,除非要写入的值是一个本机预览内存段。 - 对于所有
- 参数:
-
layout
- 要获取内存访问句柄的值布局。 - 返回:
- 新的内存段视图变量句柄。
- 抛出:
-
NullPointerException
- 如果layout
为null
。 - 自版本:
- 19
- 参见:
-
filterValue
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) filterValue
是Java平台的预览API。仅当启用预览功能时,程序才能使用filterValue
。预览功能可能会在将来的版本中被移除,或升级为Java平台的永久功能。通过使用一对过滤函数对目标变量句柄进行预处理,使其能够适应。例如,在结果变量句柄上调用
VarHandle.set(Object...)
时,传入值(类型为T
,其中T
是第一个过滤函数的最后一个参数类型)将使用第一个过滤器进行处理,然后传递给目标变量句柄。相反,在结果变量句柄上调用VarHandle.get(Object...)
时,从目标变量句柄获取的返回值(类型为T
,其中T
是第二个过滤函数的最后一个参数类型)将使用第二个过滤器进行处理并返回给调用者。更高级的访问模式类型,例如VarHandle.AccessMode.COMPARE_AND_EXCHANGE
可能同时应用两个过滤器。为了使装箱和拆箱过滤器形成良好的形式,它们的类型必须为
(A... , S) -> T
和(A... , T) -> S
,其中T
是目标变量句柄的类型。如果是这种情况,生成的变量句柄将具有类型S
,并将包含额外的坐标A...
(这些坐标将附加到目标变量句柄的坐标)。如果装箱和拆箱过滤器在调用时抛出任何已检查异常,则生成的变量句柄将抛出一个
IllegalStateException
。生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 目标变量句柄 -
filterToTarget
- 用于将某种类型S
转换为target
类型的过滤器 -
filterFromTarget
- 用于将target
类型转换为某种类型S
的过滤器 - 返回:
- 一个适配器变量句柄,接受新类型,执行提供的装箱/拆箱转换。
- 抛出:
-
IllegalArgumentException
- 如果filterFromTarget
和filterToTarget
不是形式良好的,即它们的类型不是(A... , S) -> T
和(A... , T) -> S
,其中T
是目标变量句柄的类型,或者确定filterFromTarget
或filterToTarget
在调用时抛出任何已检查异常。 -
NullPointerException
- 如果任何参数为null
。 - 自:
- 19
-
filterCoordinates
filterCoordinates
是 Java 平台的预览 API。仅当启用预览功能时,程序才能使用filterCoordinates
。预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。通过使用一元过滤函数对目标变量句柄的传入坐标值进行预处理,使其能够适应。例如,在结果变量句柄上调用
VarHandle.get(Object...)
时,从位置pos
开始的传入坐标值(类型为C1, C2 ... Cn
,其中C1, C2 ... Cn
是一元过滤函数的返回类型)将被转换为新值(类型为S1, S2 ... Sn
,其中S1, S2 ... Sn
是一元过滤函数的参数类型),然后传递(连同任何未被调整的坐标)给目标变量句柄。为了使坐标过滤器形成良好的形式,它们的类型必须为
S1 -> T1, S2 -> T1 ... Sn -> Tn
,其中T1, T2 ... Tn
是从位置pos
开始的目标变量句柄的坐标类型。如果任何过滤器在调用时抛出已检查异常,则生成的变量句柄将抛出一个
IllegalStateException
。生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 目标变量句柄 -
pos
- 要转换的第一个坐标的位置 -
filters
- 用于转换从位置pos
开始的坐标的一元函数 - 返回:
- 一个适配器变量句柄,接受新的坐标类型,将提供的转换应用于新的坐标值。
- 抛出:
-
IllegalArgumentException
- 如果filters
中的句柄不是形式良好的,即它们的类型不是S1 -> T1, S2 -> T2, ... Sn -> Tn
,其中T1, T2 ... Tn
是从位置pos
开始的目标变量句柄的坐标类型,如果pos
不在 0 到目标变量句柄坐标数量(包括)之间,或者如果提供的过滤器比从pos
开始的实际坐标类型的数量还要多,或者确定任何过滤器在调用时抛出任何已检查异常。 -
NullPointerException
- 如果任何参数为null
或filters
包含null
。 - 自:
- 19
-
insertCoordinates
insertCoordinates
是 Java 平台的预览 API。仅当启用预览功能时,程序才能使用insertCoordinates
。预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。为目标变量句柄提供一个或多个提前绑定的坐标,作为变量句柄调用的一部分。因此,生成的变量句柄将具有比目标变量句柄更少的坐标类型。例如,在结果变量句柄上调用
VarHandle.get(Object...)
时,传入的坐标值将与绑定的坐标值连接,然后传递给目标变量句柄。为了使绑定的坐标形成良好的形式,它们的类型必须为
T1, T2 ... Tn
,其中T1, T2 ... Tn
是从位置pos
开始的目标变量句柄的坐标类型。生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 插入绑定坐标后要调用的变量句柄 -
pos
- 要插入的第一个坐标的位置 -
values
- 要插入的一系列绑定坐标 - 返回:
- 一个适配器变量句柄,插入额外的坐标,然后调用目标变量句柄
- 抛出:
-
IllegalArgumentException
- 如果pos
不在 0 到目标变量句柄坐标数量(包括)之间,或者如果提供的值比从pos
开始的实际坐标类型的数量还要多。 -
ClassCastException
- 如果values
中的绑定坐标不是形式良好的,即它们的类型不是T1, T2 ... Tn
,其中T1, T2 ... Tn
是从位置pos
开始的目标变量句柄的坐标类型。 -
NullPointerException
- 如果任何参数为null
或values
包含null
。 - 自:
- 19
-
permuteCoordinates
public static VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder) permuteCoordinates
是 Java 平台的预览 API。仅当启用预览功能时,程序才能使用permuteCoordinates
。预览功能可能会在将来的版本中被移除,或升级为 Java 平台的永久功能。通过重新排列目标变量句柄的坐标值,使其新坐标与提供的坐标匹配,提供一个适配目标变量句柄的变量句柄。给定的数组控制重新排序。将传入坐标的数量(值为
newCoordinates.size()
)称为#I
,将传出坐标的数量(与目标变量句柄关联的坐标数量)称为#O
。然后,重新排序数组的长度必须为#O
,每个元素必须是小于#I
的非负数。对于小于#O
的每个N
,第N
个传出坐标将从第I
个传入坐标中取出,其中I
是reorder[N]
。不会应用坐标值转换。由
newCoordinates
确定的每个传入坐标的类型必须与目标变量句柄中相应传出坐标的类型相同。重新排序数组不需要指定实际的排列。如果其索引在数组中出现多次,则传入坐标将被复制,如果其索引未在数组中出现,则传入坐标将被丢弃。
生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 重新排序坐标后要调用的变量句柄 -
newCoordinates
- 新的坐标类型 -
reorder
- 控制重新排序的索引数组 - 返回:
- 一个适配器变量句柄,用于重新排列传入的坐标值,然后调用目标变量句柄
- 抛出:
-
IllegalArgumentException
- 如果索引数组的长度不等于目标变量句柄的坐标数量,或者如果任何索引数组元素不是newCoordinates
的有效索引,或者目标变量句柄和newCoordinates
中的两个对应坐标类型不相同。 -
NullPointerException
- 如果任何参数是null
或newCoordinates
包含null
。 - 自:
- 19
-
collectCoordinates
collectCoordinates
是Java平台的预览API。只有在启用预览功能时,程序才能使用collectCoordinates
。预览功能可能会在将来的版本中删除,或升级为Java平台的永久功能。通过使用过滤器(方法句柄)对目标变量句柄的子序列坐标值进行预处理,从而适应目标变量句柄。预处理后的坐标将被过滤函数的结果(如果有的话)替换,然后在修改后的(通常是缩短的)坐标列表上调用目标变量句柄。如果
R
是过滤器的返回类型(不能为void),则目标变量句柄必须接受类型为R
的值作为其在位置pos
的坐标,之前和/或之后是未传递给过滤器的任何坐标。不会重新排序坐标,并且从适配器传递给目标的坐标子序列中的整个坐标子序列将被过滤器的返回值替换(按顺序)。过滤器的参数类型(如果有)将替换目标变量句柄的零个或一个坐标类型,在位置
pos
处,在生成的适配变量句柄中。过滤器的返回类型必须与目标变量句柄在位置pos
处的坐标类型相同,并且目标变量句柄的坐标由过滤器的返回值提供。如果在调用任何过滤器时抛出已检查异常,则生成的变量句柄将抛出一个
IllegalStateException
。生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 在过滤坐标后要调用的变量句柄 -
pos
- 要过滤的坐标的位置 -
filter
- 过滤器方法句柄 - 返回:
- 一个适配器变量句柄,用于在调用目标变量句柄之前过滤传入的坐标值
- 抛出:
-
IllegalArgumentException
- 如果filter
的返回类型为void,或者与目标变量句柄的pos
坐标不同,如果pos
不在0和目标变量句柄坐标数量之间(包括)之间,如果生成的变量句柄的类型将具有太多坐标,或者确定filter
会抛出任何已检查异常。 -
NullPointerException
- 如果任何参数是null
。 - 自:
- 19
-
dropCoordinates
dropCoordinates
是Java平台的预览API。只有在启用预览功能时,程序才能使用dropCoordinates
。预览功能可能会在将来的版本中删除,或升级为Java平台的永久功能。返回一个变量句柄,将在委托给目标变量句柄之前丢弃一些虚拟坐标。因此,生成的变量句柄将具有比目标变量句柄更多的坐标类型。pos
参数可以在零和N之间取值,其中N是目标变量句柄的坐标类型的数量。如果pos
为零,则虚拟坐标将位于目标的真实参数之前;如果pos
为N,则它们将在其后。生成的变量句柄将具有与目标变量句柄相同的访问模式(参见
VarHandle.AccessMode
)和原子访问保证。- 参数:
-
target
- 在丢弃虚拟坐标后要调用的变量句柄 -
pos
- 要丢弃的第一个坐标的位置(最左边为零) -
valueTypes
- 要丢弃的坐标的类型 - 返回:
- 一个适配器变量句柄,用于在调用目标变量句柄之前丢弃一些虚拟坐标
- 抛出:
-
IllegalArgumentException
- 如果pos
不在0和目标变量句柄坐标数量之间(包括)。 -
NullPointerException
- 如果任何参数是null
或valueTypes
包含null
。 - 自:
- 19
-
memorySegmentViewVarHandle
。