用于简化创建实现一个或多个接口的简单“函数对象”的方法,通过委托给提供的MethodHandle,可能在对参数进行类型适配和部分求值后。这些方法通常用作invokedynamic调用点的引导方法,以支持Java编程语言的lambda表达式和方法引用表达式功能。
通过提供的MethodHandle指定的行为的间接访问将按顺序进行三个阶段:
-
链接发生在调用此类中的方法时。它们的参数包括要实现的接口(通常是一个函数式接口,即只有一个抽象方法的接口)、要实现的该接口的方法的名称和签名、描述所需实现行为的
MethodHandle,以及可能的其他附加元数据,并生成一个可用于创建适当的函数对象的CallSite。链接可能涉及动态加载实现目标接口的新类,或重用适当的现有类。
CallSite可以被视为函数对象的“工厂”,因此这些链接方法被称为“元工厂”。 -
捕获发生在调用
CallSite的目标时,通常通过invokedynamic调用点,生成一个函数对象。对于单个工厂CallSite,这可能发生多次。如果行为
MethodHandle除了指定接口方法之外还有其他参数,这些参数被称为捕获参数,必须作为参数提供给CallSite的目标。在链接期间确定捕获参数的预期数量和类型。捕获可能涉及分配新的函数对象,也可能返回一个适当的现有函数对象。由捕获产生的函数对象的身份是不可预测的,因此对身份敏感的操作(如引用相等性、对象锁定和
System.identityHashCode())在不同的实现中可能产生不同的结果,甚至在同一实现中的不同调用中也可能产生不同的结果。 -
调用发生在对函数对象上的实现接口方法进行调用时。对于单个函数对象,这可能发生多次。调用实现
MethodHandle引用的方法,将捕获的参数和调用参数传递给它。返回方法的结果。
有时限制调用时允许的输入或结果集是有用的。例如,当泛型接口Predicate<T>被参数化为Predicate<String>时,输入必须是一个String,即使要实现的方法允许任何Object。在链接时,附加的MethodType参数描述了“动态”方法类型;在调用时,参数和最终结果将与此MethodType进行检查。
此类提供两种形式的链接方法:标准版本(metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType))使用优化的协议,以及另一版本altMetafactory(MethodHandles.Lookup, String, MethodType, Object...))。另一版本是标准版本的泛化,通过标志和附加参数提供了对生成的函数对象行为的额外控制。另一版本增加了管理函数对象以下属性的能力:
- 多个方法。有时实现多个方法签名的变体是有用的,涉及参数或返回类型的适配。当方法的多个不同VM签名在逻辑上被语言视为相同方法时,会发生这种情况。标志
FLAG_BRIDGES表示将提供一系列额外的MethodType,每个都将由生成的函数对象实现。这些方法将共享相同的名称和实例化类型。 - 多个接口。如果需要,函数对象可以实现多个接口。(这些附加接口通常是没有方法的标记接口。)标志
FLAG_MARKERS表示将提供一系列额外的接口,每个都应该由生成的函数对象实现。 - 可序列化性。生成的函数对象通常不支持序列化。如果需要,可以使用
FLAG_SERIALIZABLE指示函数对象应该是可序列化的。可序列化的函数对象将使用作为它们的序列化形式的SerializedLambda类的实例,这需要来自捕获类(由MethodHandles.Lookup参数caller描述的类)的额外帮助;有关详细信息,请参阅SerializedLambda。
假设链接参数如下:
factoryType(描述CallSite签名)具有K个类型为(D1..Dk)的参数和返回类型Rd;interfaceMethodType(描述实现的方法类型)具有N个类型为(U1..Un)的参数和返回类型Ru;implementation(提供实现的MethodHandle)具有M个类型为(A1..Am)的参数和返回类型Ra(如果方法描述为实例方法,则此方法句柄的方法类型已经包括与接收者对应的额外第一个参数);dynamicMethodType(允许调用时的限制)具有N个类型为(T1..Tn)的参数和返回类型Rt。
那么以下链接不变性必须成立:
interfaceMethodType和dynamicMethodType具有相同的参数个数N,对于i=1..N,Ti和Ui是相同的类型,或者Ti和Ui都是引用类型且Ti是Ui的子类型- Rt和Ru要么是相同类型,要么都是引用类型且Rt是Ru的子类型
- K + N = M
- 对于i=1..K,Di = Ai
- 对于i=1..N,Ti可以适配为Aj,其中j=i+k
- 返回类型Rt为void,或者返回类型Ra不是void且可以适配为Rt
此外,在捕获时,如果implementation对应于实例方法,并且有任何捕获参数(K > 0),则第一个捕获参数(对应于接收者)必须为非null。
类型Q被认为适配为S如下:
| Q | S | 链接时检查 | 调用时检查 |
|---|---|---|---|
| 基本类型 | 基本类型 | Q可以通过基本类型扩展转换为S | 无 |
| 基本类型 | 引用类型 | S是Wrapper(Q)的超类型 | 从Wrapper(Q)到S的转换 |
| 引用类型 | 基本类型 | 对于参数类型:Q是一个基本类型包装类,基本类型(Q)可以扩展为S 对于返回类型:如果Q是一个基本类型包装类,检查基本类型(Q)是否可以扩展为S |
如果Q不是基本类型包装类,则将Q转换为基本类型S的基类;例如,数值类型的Number |
| 引用类型 | 引用类型 | 对于参数类型:S是Q的超类型 对于返回类型:无 |
从Q到S的转换 |
- API注释:
-
这些链接方法旨在支持Java语言中lambda表达式和方法引用的评估。对于源代码中的每个lambda表达式或方法引用,都有一个目标类型,即一个函数式接口。评估lambda表达式会产生其目标类型的对象。评估lambda表达式的推荐机制是将lambda主体解糖为一个方法,调用一个invokedynamic调用点,其静态参数列表描述函数式接口的唯一方法和解糖的实现方法,并返回一个实现目标类型的对象(lambda对象)。(对于方法引用,实现方法只是引用的方法;不需要解糖。)
实现方法的参数列表和接口方法的参数列表可能在几个方面有所不同。实现方法可能有额外的参数以适应lambda表达式捕获的参数;还可能由于参数的允许适配(如强制转换、装箱、拆箱和基本类型扩展)而产生差异。(可变参数适配不由元工厂处理;这些预期由调用方处理。)
invokedynamic调用点有两个参数列表:静态参数列表和动态参数列表。静态参数列表存储在常量池中;动态参数在捕获时推送到操作数栈上。引导方法可以访问整个静态参数列表(在本例中,包括描述实现方法、目标接口和目标接口方法的信息),以及描述动态参数的数量和静态返回类型的方法签名(但不包括值)以及invokedynamic站点的静态返回类型。
实现方法使用引用方法或构造方法的直接方法句柄描述。理论上,可以使用任何方法句柄,但这与某些实现技术不兼容,并且会使实现必须进行的工作变得复杂。
- 自Java版本:
- 1.8
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final int替代元工厂的标志,指示lambda对象需要调用implementation的其他方法static final intstatic final int -
Method Summary
Modifier and TypeMethodDescriptionstatic CallSitealtMetafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, Object... args) 通过将适当类型适配和部分评估参数后,促进创建简单的“函数对象”,这些对象通过委托给提供的MethodHandle来实现一个或多个接口。static CallSitemetafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, MethodType interfaceMethodType, MethodHandle implementation, MethodType dynamicMethodType) 通过将适当类型适配和部分评估参数后,促进创建简单的“函数对象”,这些对象通过委托给提供的MethodHandle来实现一个或多个接口。
-
Field Details
-
FLAG_SERIALIZABLE
public static final int FLAG_SERIALIZABLE标志,表示lambda对象必须可序列化,用于altMetafactory(java.lang.invoke.MethodHandles.Lookup, java.lang.String, java.lang.invoke.MethodType, java.lang.Object...)- 参见:
-
FLAG_MARKERS
public static final int FLAG_MARKERS标志,表示lambda对象实现了除Serializable之外的其他接口,用于altMetafactory(java.lang.invoke.MethodHandles.Lookup, java.lang.String, java.lang.invoke.MethodType, java.lang.Object...)- 参见:
-
FLAG_BRIDGES
public static final int FLAG_BRIDGES用于指示lambda对象需要调用implementation的其他方法的替代元工厂的标志- 参见:
-
-
Method Details
-
metafactory
public static CallSite metafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, MethodType interfaceMethodType, MethodHandle implementation, MethodType dynamicMethodType) throws LambdaConversionException 通过将适当类型适配和部分评估参数后,促进创建简单的“函数对象”,这些对象通过委托给提供的MethodHandle来实现一个或多个接口。通常用作invokedynamic调用点的引导方法,以支持Java编程语言的lambda表达式和方法引用表达式功能。这是标准的、简化的元工厂;
altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)提供了额外的灵活性。有关此方法行为的一般描述,请参见上面。当从此方法返回的
CallSite的目标被调用时,生成的函数对象是实现了factoryType命名的接口的类的实例,声明了由interfaceMethodName给出的名称和interfaceMethodType给出的签名的方法。它还可以覆盖来自Object的其他方法。- 参数:
-
caller- 表示具有调用者的访问权限的查找上下文。具体来说,查找上下文必须具有完全特权访问。在与invokedynamic一起使用时,VM会自动堆叠这个权限。 -
interfaceMethodName- 要实现的方法的名称。在与invokedynamic一起使用时,这是由InvokeDynamic结构的NameAndType提供的,并由VM自动堆叠。 -
factoryType-CallSite的预期签名。参数类型表示捕获变量的类型;返回类型是要实现的接口。在与invokedynamic一起使用时,这是由InvokeDynamic结构的NameAndType提供的,并由VM自动堆叠。 -
interfaceMethodType- 要由函数对象实现的方法的签名和返回类型。 -
implementation- 描述应在调用时调用的实现方法的直接方法句柄(通过适当调整参数类型和返回类型,并在调用参数之前添加捕获的参数)。 -
dynamicMethodType- 调用时应动态强制执行的签名和返回类型。在简单用例中,这与interfaceMethodType相同。 - 返回:
-
一个
CallSite,其目标可用于执行捕获,生成由factoryType命名的接口的实例 - 抛出:
-
LambdaConversionException- 如果caller没有完全特权访问权限,或者interfaceMethodName不是有效的JVM方法名称,或者factoryType的返回类型不是接口,或者implementation不是引用方法或构造函数的直接方法句柄,或者违反了链接不变性,如上所定义。 -
NullPointerException- 如果任何参数为null。 -
SecurityException- 如果存在安全管理器,并且它拒绝caller对implementation包的访问。
-
altMetafactory
public static CallSite altMetafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, Object... args) throws LambdaConversionException 便于通过委托到提供的MethodHandle来实现一个或多个接口的简单“函数对象”的创建,经过适当的类型适配和参数部分求值。通常用作invokedynamic调用点的引导方法,以支持Java编程语言的lambda表达式和方法引用表达式功能。这是通用的、更灵活的元工厂;一个简化版本由
metafactory(java.lang.invoke.MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)提供。关于此方法的行为的一般描述在LambdaMetafactory中提供。此方法的参数列表包括三个固定参数,对应于VM自动为
invokedynamic调用中的引导方法堆栈的参数,以及一个包含附加参数的Object[]参数。此方法的声明参数列表如下:CallSite altMetafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, Object... args)但其行为就好像参数列表如下:
CallSite altMetafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType, MethodType interfaceMethodType, MethodHandle implementation, MethodType dynamicMethodType, int flags, int altInterfaceCount, // 如果flags设置了MARKERS Class... altInterfaces, // 如果flags设置了MARKERS int altMethodCount, // 如果flags设置了BRIDGES MethodType... altMethods // 如果flags设置了BRIDGES )出现在
metafactory(java.lang.invoke.MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)参数列表中的参数与该方法中的规范相同。附加参数解释如下:flags表示附加选项;这是所需标志的按位或。定义的标志有FLAG_BRIDGES、FLAG_MARKERS和FLAG_SERIALIZABLE。altInterfaceCount是函数对象应该实现的附加接口数量,仅在设置了FLAG_MARKERS标志时存在。altInterfaces是一个变长的附加接口列表,其长度等于altInterfaceCount,仅在设置了FLAG_MARKERS标志时存在。altMethodCount是函数对象应该实现的附加方法签名数量,仅在设置了FLAG_BRIDGES标志时存在。altMethods是一个变长的附加方法签名列表,其长度等于altMethodCount,仅在设置了FLAG_BRIDGES标志时存在。
由
altInterfaces命名的每个类都受到与factoryType的返回类型Rd相同的限制,如上所述。由altMethods命名的每个MethodType都受到与interfaceMethodType相同的限制,如上所述。当在
flags中设置了FLAG_SERIALIZABLE时,函数对象将实现Serializable,并且将具有返回适当的SerializedLambda的writeReplace方法。caller类必须具有适当的$deserializeLambda$方法,如SerializedLambda中所述。当从此方法返回的
CallSite的目标被调用时,生成的函数对象是具有以下属性的类的实例:- 该类实现了由
factoryType的返回类型命名的接口以及由altInterfaces命名的任何接口 - 该类声明了由
interfaceMethodName给出的名称的方法,以及由interfaceMethodType给出的签名和由altMethods给出的附加签名 - 该类可以覆盖来自
Object的方法,并且可以实现与序列化相关的方法。
- 参数:
-
caller- 表示具有调用者的访问权限的查找上下文。具体来说,查找上下文必须具有full privilege access。当与invokedynamic一起使用时,这将被VM自动堆栈。 -
interfaceMethodName- 要实现的方法的名称。当与invokedynamic一起使用时,这由InvokeDynamic结构的NameAndType提供,并由VM自动堆栈。 -
factoryType-CallSite的预期签名。参数类型表示捕获变量的类型;返回类型是要实现的接口。当与invokedynamic一起使用时,这由InvokeDynamic结构的NameAndType提供,并由VM自动堆栈。 -
args- 包含所需参数interfaceMethodType、implementation、dynamicMethodType、flags和任何可选参数的Object数组,如上所述 - 返回:
-
一个
CallSite,其目标可用于执行捕获,生成由factoryType命名的接口的实例 - 抛出:
-
LambdaConversionException- 如果caller没有完全特权访问权限,或者interfaceMethodName不是有效的JVM方法名称,或者factoryType的返回类型不是接口,或者任何altInterfaces不是接口,或者implementation不是引用方法或构造函数的直接方法句柄,或者违反了链接不变量,如LambdaMetafactory中定义的。 -
NullPointerException- 如果任何参数或args的任何组件为null。 -
IllegalArgumentException- 如果args的组件数量或类型不遵循上述规则,或者altInterfaceCount或altMethodCount为负整数。 -
SecurityException- 如果存在安全管理器,并且它拒绝caller对implementation包的访问。
-