- java.lang.Object
-
- java.lang.invoke.MethodHandle
-
public abstract class MethodHandle extends Object
方法句柄是对基础方法,构造函数,字段或类似的低级操作的类型化,直接可执行的引用,具有可选的参数或返回值转换。 这些转换是相当普遍的,并且包括这样的模式为conversion , insertion , deletion和substitution 。方法处理内容
方法句柄根据其参数和返回类型进行动态和强类型化。 它们的名称或其基础方法的定义类没有区分。 必须使用与方法句柄自己的type descriptor匹配的符号类型描述符来调用方法句柄。每个方法句柄都通过
type
访问器报告其类型描述符。 此类型描述符是MethodType
对象,其结构是一系列类,其中一个是方法的返回类型(如果没有,void.class
)。方法句柄的类型控制它接受的调用类型以及适用于它的变换类型。
方法句柄包含一对称为
invokeExact
和invoke
的特殊调用方法。 两种调用方法都提供对方法句柄的底层方法,构造函数,字段或其他操作的直接访问,这些操作由参数和返回值的转换修改。 两个调用者都接受与方法句柄自己的类型完全匹配的调用。 简单,不精确的调用者也接受一系列其他呼叫类型。方法句柄是不可变的,没有可见状态。 当然,它们可以绑定到表现状态的基础方法或数据。 对于Java内存模型,任何方法句柄的行为都好像它的所有(内部)字段都是最终变量。 这意味着应用程序可见的任何方法句柄将始终完全形成。 即使方法句柄是通过数据争用中的共享变量发布的,也是如此。
方法句柄不能由用户子类化。 实现可能(或可能不)创建
MethodHandle
内部子类,这可以通过Object.getClass
操作看到。 程序员不应该从特定类中得出关于方法句柄的结论,因为方法句柄类层次结构(如果有的话)可能会不时地或不同供应商的实现之间发生变化。方法句柄编译
Java方法调用表达式命名invokeExact
或invoke
可以从Java源代码调用方法句柄。 从源代码的角度来看,这些方法可以接受任何参数,并且它们的结果可以转换为任何返回类型。 正式地,这是通过给出调用者方法Object
返回类型和变量arityObject
参数来实现的,但是它们具有称为签名多态的附加质量,其将这种调用自由直接连接到JVM执行堆栈。与通常的虚拟方法一样,对
invokeExact
和invoke
源级调用将编译为invokevirtual
指令。 更不寻常的是,编译器必须记录实际的参数类型,并且可能不会对参数执行方法调用转换。 相反,它必须根据自己未转换的类型生成将它们压入堆栈的指令。 方法句柄对象本身在参数之前被压入堆栈。 然后,编译器生成invokevirtual
指令,该指令使用描述参数和返回类型的符号类型描述符调用方法句柄。要发出完整的符号类型描述符,编译器还必须确定返回类型。 这是基于对方法调用表达式的
Object
(如果有),或者如果调用是表达式void
,否则为void
如果调用是语句)。 演员表可能是原始类型(但不是void
)。作为极端情况,未发布的
null
参数被赋予符号类型描述符java.lang.Void
。Void
类型的Void
是无害的,因为除了null引用之外没有类型Void
的引用。方法句柄调用
第一次执行invokevirtual
指令时,它通过符号解析指令中的名称并验证方法调用是静态合法的来链接。 这也适用于invokeExact
和invoke
调用。 在这种情况下,将检查编译器发出的符号类型描述符的语法是否正确,并解析它包含的名称。 因此,只要符号类型描述符在语法上格式良好并且存在类型,则调用方法句柄的invokevirtual
指令将始终链接。在链接之后执行
invokevirtual
,JVM首先检查接收方法句柄的类型,以确保它与符号类型描述符匹配。 如果类型匹配失败,则意味着调用者正在调用的方法不存在于被调用的单个方法句柄上。在
invokeExact
的情况下,调用的类型描述符(在解析符号类型名称之后)必须与接收方法句柄的方法类型完全匹配。 在普通的,不精确的invoke
的情况下,解析的类型描述符必须是接收器的asType
方法的有效参数。 因此,普通invoke
比invokeExact
更invokeExact
。在类型匹配之后,直接调用
invokeExact
并立即调用方法句柄的底层方法(或其他行为,视情况而定)。如果调用者指定的符号类型描述符与方法句柄自己的类型完全匹配,则对
invokeExact
的调用与调用invoke
工作方式相同。 如果存在类型不匹配,则invoke
尝试调整接收方法句柄的类型,就好像通过调用asType
一样 ,获取完全可调用的方法句柄M2
。 这允许在调用者和被调用者之间进行更强大的方法类型协商。( 注意:调整后的方法句柄
M2
不能直接观察,因此不需要实现来实现它。)调用检查
在典型的程序中,方法句柄类型匹配通常会成功。 但是,如果地址不匹配时,JVM将抛出一个WrongMethodTypeException
,直接(在的情况下invokeExact
)或间接仿佛被呼叫失败到asType
(在的情况下invoke
)。因此,在静态类型的程序中可能显示为链接错误的方法类型不匹配可以在使用方法句柄的程序中显示为动态
WrongMethodTypeException
。由于方法类型包含“实时”
类
对象,因此方法类型匹配会同时考虑类型名称和类加载器。 因此,即使在一个类加载器L1
创建方法句柄M
并在另一个L2
,方法句柄调用也是类型安全的,因为调用者的符号类型描述符(在L2
中已解析)与原始被调用方法的符号类型匹配描述符,如L1
。 决议L1
时发生M
被创建,其类型分配,而在分辨率L2
当发生invokevirtual
指令链接。除了类型描述符检查之外,方法句柄调用其基础方法的能力是不受限制的。 如果有权访问该方法的类在非公共方法上形成方法句柄,则任何接收对它的引用的调用者都可以在任何地方使用生成的句柄。
与Core Reflection API不同,每次调用反射方法时都会检查访问权限,执行方法句柄访问检查when the method handle is created 。 在
ldc
(见下文)的情况下,访问检查作为链接常量方法句柄下的常量池条目的一部分执行。因此,非公共方法或非公共类中的方法的处理通常应该保密。 除非从不受信任的代码中使用它们是无害的,否则不应将它们传递给不受信任的代码。
方法句柄创建
Java代码可以创建一个方法句柄,直接访问该代码可访问的任何方法,构造函数或字段。 这是通过一个名为MethodHandles.Lookup
的基于功能的反射API 完成的 。 例如,可以从Lookup.findStatic
获得静态方法句柄。 还有Core Reflection API对象的转换方法,例如Lookup.unreflect
。与类和字符串一样,对应于可访问字段,方法和构造函数的方法句柄也可以直接在类文件的常量池中表示为由
ldc
字节码加载的ldc
。 一种新型的常量存储库项,的CONSTANT_MethodHandle
,直接指的是相关的CONSTANT_Methodref
,CONSTANT_InterfaceMethodref
,或CONSTANT_Fieldref
常量存储库项。 (有关方法句柄常量的完整详细信息,请参阅Java虚拟机规范的4.4.8和5.4.3.5节。)通过查找或来自方法或构造函数的常量加载生成的方法句柄,其变量arity修饰符位(
0x0080
)具有相应的变量arity,就好像它们是在asVarargsCollector
或withVarargs
的帮助下定义的一样 。方法参考可以指静态或非静态方法。 在非静态情况下,方法句柄类型包括显式接收器参数,在任何其他参数之前添加。 在方法句柄的类型中,根据最初请求方法的类来键入初始接收器参数。 (例如,如果通过
ldc
获得非静态方法句柄,则接收器的类型是常量池条目中指定的类。)方法句柄常量受相同的链接时访问检查其对应的字节码指令,如果字节码行为会引发此类错误,则
ldc
指令将抛出相应的链接错误。作为其必然结果,对受保护成员的访问仅限于访问类或其子类之一的接收者,并且访问类必须又是受保护成员的定义类的子类(或包兄弟)。 如果方法引用引用当前包之外的类的受保护的非静态方法或字段,则receiver参数将缩小为访问类的类型。
当调用虚方法的方法句柄时,始终在接收器中查找该方法(即第一个参数)。
还可以创建特定虚拟方法实现的非虚方法句柄。 这些不基于接收器类型执行虚拟查找。 这样的方法句柄模拟
invokespecial
指令对同一方法的影响。 还可以创建非虚方法句柄,以模拟invokevirtual
或invokeinterface
指令对私有方法的影响(如果适用)。用法示例
以下是一些用法示例:Object x, y; String s; int i; MethodType mt; MethodHandle mh; MethodHandles.Lookup lookup = MethodHandles.lookup(); // mt is (char,char)String mt = MethodType.methodType(String.class, char.class, char.class); mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy",'d','n'); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; assertEquals(s, "nanny"); // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); assertEquals(s, "savvy"); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); x = mh.invoke("one", "two"); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object)1, (Object)2, (Object)3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList(1,2,3)); // mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); // invokeExact(Ljava/util/List;)I assert(i == 3); mt = MethodType.methodType(void.class, String.class); mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
invokeExact
或plaininvoke
每个调用invoke
生成一个invoke
指令,其中符号类型描述符在以下注释中指示。 在这些示例中,假设辅助方法assertEquals
是对其参数调用Objects.equals
的方法,并断言结果为true。例外
方法invokeExact
和invoke
被声明为抛出Throwable
,也就是说方法句柄可以抛出的内容没有静态限制。 由于JVM不区分已检查和未检查的异常(当然除了它们的类之外),因此将检查的异常归因于方法句柄调用对字节码形状没有特别的影响。 但是在Java源代码中,执行方法句柄调用的方法必须显式抛出Throwable
,否则必须在本地捕获所有throwable,重新抛出那些在上下文中合法的东西,并包装那些非法的抛出。签名多态性
invokeExact
和plaininvoke
的异常编译和链接行为由术语签名多态性引用。 如Java语言规范中所定义的,签名多态方法是可以使用各种呼叫签名和返回类型操作的方法。在源代码中,无论请求的符号类型描述符如何,都将编译对签名多态方法的调用。 像往常一样,Java编译器使用给定的符号类型描述符针对命名方法发出
invokevirtual
指令。 不寻常的部分是符号类型描述符是从实际的参数和返回类型派生的,而不是从方法声明派生的。当JVM处理包含签名多态调用的字节码时,无论其符号类型描述符如何,它都将成功链接任何此类调用。 (为了保持类型安全性,JVM将使用适当的动态类型检查来保护此类调用,如其他地方所述。)
字节码生成器(包括编译器后端)需要为这些方法发出未转换的符号类型描述符。 确定符号链接的工具需要接受这种未转换的描述符,而不报告链接错误。
方法句柄和Core Reflection API之间的互操作
使用Lookup
API中的工厂方法,可以将Core Reflection API对象表示的任何类成员转换为行为等效的方法句柄。 例如,可以使用Lookup.unreflect
将反射方法
转换为方法句柄。 生成的方法句柄通常提供对底层类成员的更直接和有效的访问。作为特殊情况,当Core Reflection API用于查看
invokeExact
的签名多态方法invokeExact
或plaininvoke
时,它们看起来像普通的非多态方法。 如Class.getDeclaredMethod
所示 ,它们的反射外观不受其在此API中的特殊状态的影响。 例如,Method.getModifiers
将准确报告任何类似声明的方法所需的那些修饰符位,包括native
和varargs
位。与任何反射方法一样,这些方法(反映时)可以通过
java.lang.reflect.Method.invoke
调用。 但是,这种反射调用不会导致方法句柄调用。 这样的调用,如果传递了必需的参数(类型为Object[]
的单个参数),将忽略该参数并抛出UnsupportedOperationException
。由于
invokevirtual
指令可以在任何符号类型描述符下本机调用方法句柄,因此该反射视图与通过字节码的这些方法的正常表示冲突。 因此,当Class.getDeclaredMethod
反思地查看时,这两种本机方法可以仅被视为占位符。要获取特定类型描述符的调用方法,请使用
MethodHandles.exactInvoker
或MethodHandles.invoker
。 对于任何指定的类型描述符,Lookup.findVirtual
API还能够返回一个方法句柄来调用invokeExact
或plaininvoke
。方法句柄和Java泛型之间的互操作
可以在使用Java泛型类型声明的方法,构造函数或字段上获取方法句柄。 与Core Reflection API一样,方法句柄的类型将从源级别类型的擦除构造。 调用方法句柄时,其参数类型或返回值强制类型可以是泛型类型或类型实例。 如果发生这种情况,编译器在构造invokevirtual
指令的符号类型描述符时将通过其擦除替换这些类型。方法句柄不代表Java参数化(通用)类型的类函数类型,因为类函数类型和参数化Java类型之间存在三种不匹配。
- 方法类型适用于所有可能的arities,从无参数到最多maximum number的允许参数。 泛型不是变量,因此不能代表这一点。
- 方法类型可以指定基本类型的参数,Java泛型类型不能超出范围。
- 方法句柄(组合器)上的高阶函数通常在各种函数类型中是通用的,包括多个元素的函数类型。 用Java类型参数表示这种通用性是不可能的。
Arity limits
JVM强加任何类型的所有方法和构造函数,绝对限制为255个堆栈参数。 在某些情况下,此限制似乎更具限制性:-
long
或double
参数计算(出于arity限制的目的)作为两个参数槽。 - 非静态方法为调用该方法的对象使用额外的参数。
- 构造函数为正在构造的对象使用额外的参数。
- 由于方法句柄
invoke
方法(或其他签名多态方法)是非虚拟的,因此除了任何非虚拟接收器对象之外,它还会为方法句柄本身消耗额外的参数。
IllegalArgumentException
。 特别是,方法句柄的类型不能具有精确最大值为255的arity。- 从以下版本开始:
- 1.7
- 另请参见:
-
MethodType
,MethodHandles
-
-
方法摘要
所有方法 实例方法 具体的方法 变量和类型 方法 描述 MethodHandle
asCollector(int collectArgPos, 类<?> arrayType, int arrayLength)
创建一个 数组收集方法句柄,它接受从给定位置开始的给定数量的位置参数,并将它们收集到数组参数中。MethodHandle
asCollector(类<?> arrayType, int arrayLength)
创建一个 数组收集方法句柄,它接受给定数量的尾随位置参数并将它们收集到数组参数中。MethodHandle
asFixedArity()
创建一个 固定的arity方法句柄,该句柄与当前方法句柄等效。MethodHandle
asSpreader(int spreadArgPos, 类<?> arrayType, int arrayLength)
创建一个 数组传播方法句柄,它接受给定位置的数组参数,并将其元素作为位置参数传播,代替数组。MethodHandle
asSpreader(类<?> arrayType, int arrayLength)
创建一个 数组传播方法句柄,它接受一个尾随数组参数并将其元素作为位置参数传播。MethodHandle
asType(MethodType newType)
生成适配器方法句柄,该句柄将当前方法句柄的类型调整为新类型。MethodHandle
asVarargsCollector(类<?> arrayType)
使 变量arity适配器能够接受任意数量的尾随位置参数并将它们收集到数组参数中。MethodHandle
bindTo(Object x)
将值x
绑定到方法句柄的第一个参数,而不调用它。Object
invoke(Object... args)
调用方法句柄,允许任何调用者类型描述符,并可选择对参数和返回值执行转换。Object
invokeExact(Object... args)
调用方法句柄,允许任何调用者类型描述符,但需要精确的类型匹配。Object
invokeWithArguments(Object... arguments)
执行变量arity调用,将给定数组中的参数传递给方法句柄,就好像通过来自调用站点的不精确invoke
,其仅提及类型Object
,并且其实际参数计数是参数数组的长度。Object
invokeWithArguments(List<?> arguments)
执行变量arity调用,将给定列表中的参数传递给方法句柄,就好像通过来自调用站点的不精确invoke
一样 ,其仅提及类型Object
,并且其实际参数计数是参数列表的长度。boolean
isVarargsCollector()
确定此方法句柄是否支持 variable arity调用。String
toString()
返回方法句柄的字符串表示形式,从字符串"MethodHandle"
开始,以方法句柄类型的字符串表示形式结束。MethodType
type()
报告此方法句柄的类型。MethodHandle
withVarargs(boolean makeVarargs)
如果布尔标志为真,则将此方法句柄调整为 variable arity ,否则为 fixed arity 。
-
-
-
方法详细信息
-
type
public MethodType type()
报告此方法句柄的类型。 通过invokeExact
对此方法句柄的每次调用invokeExact
必须与此类型完全匹配。- 结果
- 方法句柄类型
-
invokeExact
public final Object invokeExact(Object... args) throws Throwable
调用方法句柄,允许任何调用者类型描述符,但需要精确的类型匹配。 调用站点invokeExact
处的符号类型描述符必须与此方法句柄type
完全匹配。 参数或返回值不允许转换。当通过Core Reflection API观察到此方法时,它将显示为单个本机方法,获取对象数组并返回对象。 如果直接通过
java.lang.reflect.Method.invoke
,通过JNI或间接通过Lookup.unreflect
调用此本机方法,则会抛出UnsupportedOperationException
。- 参数
-
args
- 签名多态参数列表,使用varargs静态表示 - 结果
-
签名 - 多态结果,使用
Object
静态表示 - 异常
-
WrongMethodTypeException
- 如果目标的类型与调用者的符号类型描述符不同 -
Throwable
- 底层方法抛出的任何内容都会通过方法句柄调用继续传播
-
invoke
public final Object invoke(Object... args) throws Throwable
调用方法句柄,允许任何调用者类型描述符,并可选择对参数和返回值执行转换。如果调用站点的符号类型描述符与此方法句柄
type
完全匹配,则调用将按照invokeExact
进行 。否则,调用继续进行,好像首先通过调用
asType
调整此方法句柄以将该方法句柄调整为所需类型,然后调用如同invokeExact
一样在调整后的方法句柄上进行。无法保证实际发出
asType
电话。 如果JVM可以预测调用的结果,它可以直接对调用者的参数执行调整,并根据自己的确切类型调用目标方法句柄。呼叫站点
invoke
处的已解析类型描述符必须是接收器asType
方法的有效参数。 特别是,如果被调用者不是variable arity collector ,则调用者必须指定与被调用者类型相同的参数arity。当通过Core Reflection API观察到此方法时,它将显示为单个本机方法,获取对象数组并返回对象。 如果直接通过
java.lang.reflect.Method.invoke
,通过JNI或间接通过Lookup.unreflect
调用此本机方法,则会抛出UnsupportedOperationException
。- 参数
-
args
- 签名多态参数列表,使用varargs静态表示 - 结果
-
签名 - 多态结果,使用
Object
静态表示 - 异常
-
WrongMethodTypeException
- 如果无法将目标的类型调整为调用者的符号类型描述符 -
ClassCastException
- 如果可以将目标的类型调整为调用者,但引用ClassCastException
失败 -
Throwable
- 底层方法抛出的任何内容都会通过方法句柄调用继续传播
-
invokeWithArguments
public Object invokeWithArguments(Object... arguments) throws Throwable
执行变量arity调用,将给定数组中的参数传递给方法句柄,就好像通过来自调用站点的不精确invoke
,其仅提及类型Object
,并且其实际参数计数是参数数组的长度。具体而言,执行就像通过以下步骤一样进行,尽管如果JVM可以预测它们的影响,则不能保证调用这些方法。
- 确定参数数组的长度为
N
。 对于空引用,N=0
。 - 将数组的
N
元素收集为逻辑参数列表,每个参数静态类型为Object
。 - 确定,作为
M
,此方法句柄的类型的参数计数。 - 确定
N
参数的一般类型TN
或M
参数(如果小于N
,如TN=MethodType.genericMethodType(Math.min(N, M))
。 - 如果
N
大于M
,请执行以下检查和操作以缩短逻辑参数列表:- 检查此方法句柄是否具有变量arity,其中trailing parameter是某种数组类型
A[]
。 如果没有,请使用WrongMethodTypeException
。 - 收集后缘元件(有
N-M+1
他们的)从逻辑参数列表成型的单个阵列A[]
,使用asType
转换到每个后参数输入转换A
。 - 如果任何这些转换的证明是不可能的,会失败,无论是
ClassCastException
如果任何尾随元件不能被转换为A
或NullPointerException
如果任何尾随元件是null
和A
是不是引用类型。 - 将收集到
A[]
类型数组中的逻辑参数替换为数组本身,从而将参数列表缩短为长度M
。 此最终参数保留静态类型A[]
。 - 调节型
TN
通过改变N
个参数类型从Object
到A[]
。
- 检查此方法句柄是否具有变量arity,其中trailing parameter是某种数组类型
- 强制原始目标方法句柄
MH0
为所需类型,如MH1 = MH0.asType(TN)
。 - 将参数列表传播到
N
单独的参数A0, ...
。 - 在解压缩的参数上调用类型调整的方法句柄:MH1.invokeExact(A0,...)。
- 将返回值作为
Object
参考。
如果目标方法句柄具有变量arity,并且参数列表比该arity长,那么从尾随数组参数的位置开始的多余参数将被收集(如果可能,就像通过
asType
转换一样)到一个数组中适当的类型,调用将在缩短的参数列表上进行。 通过这种方式,仍可以统一处理扩展到超过254个时隙的巨型参数列表 。与可以“循环”数组参数的
generic
调用模式(将其直接传递给目标方法)不同,此调用模式始终会创建一个新的数组参数,即使传递给invokeWithArguments
的原始数组可以作为直接参数接受目标方法。 即使实际参数的数字M
是arityN
,并且最后一个参数动态地是类型A[]
的合适数组,它仍将被装箱成新的单元素数组,因为调用站点将参数静态地键入为Object
,不是数组类型。 这不是此方法的特殊规则,而是rules for variable-arity invocation的常规效果。由于
asType
步骤的操作,将根据需要应用以下参数转换:- 参考铸造
- 拆箱
- 扩大原始转换
- 变量转换
如果调用返回原语,则调用返回的结果为boxed;如果返回类型为void,则强制为null。
与签名多态方法
invokeExact
和invoke
,invokeWithArguments
可以通过Core Reflection API和JNI正常访问。 因此,它可以用作本机或反射代码和方法句柄之间的桥梁。- API Note:
-
此调用大致相当于以下代码:
// for jumbo argument lists, adapt varargs explicitly: int N = (arguments == null? 0: arguments.length); int M = this.type.parameterCount(); int MAX_SAFE = 127; // 127 longs require 254 slots, which is OK if (N > MAX_SAFE && N > M && this.isVarargsCollector()) { Class<?> arrayType = this.type().lastParameterType(); Class<?> elemType = arrayType.getComponentType(); if (elemType != null) { Object args2 = Array.newInstance(elemType, M); MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType); for (int i = 0; i < M; i++) { arraySetter.invoke(args2, i, arguments[M-1 + i]); } arguments = Arrays.copyOf(arguments, M); arguments[M-1] = args2; return this.asFixedArity().invokeWithArguments(arguments); } } // done with explicit varargs processing // Handle fixed arity and non-jumbo variable arity invocation. MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0); Object result = invoker.invokeExact(this, arguments);
- 参数
-
arguments
- 传递给目标的参数 - 结果
- 目标返回的结果
- 异常
-
ClassCastException
- 如果参数转换无法转换参数 -
WrongMethodTypeException
- 如果无法调整目标的类型以获取给定数量的Object
参数 -
Throwable
- 目标方法调用抛出的任何内容 - 另请参见:
-
MethodHandles.spreadInvoker(java.lang.invoke.MethodType, int)
- 确定参数数组的长度为
-
invokeWithArguments
public Object invokeWithArguments(List<?> arguments) throws Throwable
执行变量arity调用,将给定列表中的参数传递给方法句柄,就好像通过来自调用站点的不精确invoke
,其仅提及类型Object
,并且其实际参数计数是参数列表的长度。此方法也等效于以下代码:
invokeWithArguments(arguments.toArray())
如果此方法句柄具有可变arity,则可以接受巨型大小的列表。 有关详细信息,请参见
invokeWithArguments(Object[])
。- 参数
-
arguments
- 传递给目标的参数 - 结果
- 目标返回的结果
- 异常
-
NullPointerException
- 如果arguments
是空引用 -
ClassCastException
- 如果参数转换无法转换参数 -
WrongMethodTypeException
- 如果无法调整目标的类型以获取给定数量的Object
参数 -
Throwable
- 目标方法调用抛出的任何内容
-
asType
public MethodHandle asType(MethodType newType)
生成适配器方法句柄,该句柄将当前方法句柄的类型调整为新类型。 保证生成的方法句柄报告一个等于所需新类型的类型。如果原始类型和新类型相等,则返回
this
。调用新方法句柄时,将执行以下步骤:
- 转换传入参数列表以匹配原始方法句柄的参数列表。
- 在已转换的参数列表上调用原始方法句柄。
- 将原始方法句柄返回的任何结果转换为新方法句柄的返回类型。
这种方法提供了
invokeExact
与普通,不精确invoke
之间的关键行为差异。 当调用者的类型描述符与被调用者完全匹配时,这两个方法执行相同的步骤,但是当类型不同时,普通invoke
也调用asType
(或一些内部等价物)以匹配调用者和被调用者的类型。如果当前方法是一个变量arity方法,则句柄参数列表转换可能涉及将几个参数转换并收集到一个数组中,如described elsewhere 。 在所有其他情况下,所有转换都是成对应用的,这意味着每个参数或返回值都只转换为一个参数或返回值(或没有返回值)。 应用的转换是通过查询旧方法句柄类型和新方法句柄类型的相应组件类型来定义的。
设T0和T1为对应的新旧参数类型,或旧的和新的返回类型。 具体而言,对于某些有效索引
i
,设T0=newType.parameterType(i)
和T1=this.type().parameterType(i)
。 或者,以其他方式返回值,让T0=this.type().returnType()
和T1=newType.returnType()
。 如果类型相同,则新方法句柄不会更改相应的参数或返回值(如果有)。 否则,如果可能,将应用以下转换之一:- 如果T0和T1是引用,则应用转换为T1 。 (这些类型不需要以任何特定方式相关。这是因为null的动态值可以转换为任何引用类型。)
- 如果T0和T1是基元,则应用Java方法调用转换(JLS 5.3)(如果存在)。 (具体来说, T0必须通过扩展原语转换转换为T1 。)
- 如果T0是基元并且T1是引用,则应用Java转换(JLS 5.5)(如果存在)。 (具体来说,该值从T0加载到其包装类,然后根据需要将其加宽到T1 。)
- 如果T0是引用而T1是原语,则将在运行时应用取消装箱转换,之后可能会对原始值进行Java方法调用转换(JLS 5.3)。 (这些是原始的扩展转换。) T0必须是包装类或超类型。 (在T0是Object的情况下,这些是
java.lang.reflect.Method.invoke
允许的转换。)取消装箱转换必须有成功的可能性,这意味着如果T0本身不是包装类,则必须存在至少一个包装类TW ,是T0的子类型,其未装箱的原始值可以扩展为T1 。 - 如果返回类型T1标记为void,则丢弃任何返回的值
- 如果返回类型T0为空且T1为引用,则引入空值。
- 如果返回类型T0为空并且T1为基元,则引入零值。
如果无法进行任何一个所需的成对转换,则无法进行方法句柄转换。
在运行时,应用于引用参数或返回值的转换可能需要额外的运行时检查,这可能会失败。 取消装箱操作可能会失败,因为原始引用为空,从而导致
NullPointerException
。 取消装箱操作或参考演员也可能在对错误类型的对象的引用上失败,从而导致ClassCastException
。 虽然取消装箱操作可能会接受几种包装,但如果没有,则会抛出ClassCastException
。- 参数
-
newType
- 新方法句柄的预期类型 - 结果
-
在执行任何必要的参数转换后委派给
this
的方法句柄,并安排任何必要的返回值转换 - 异常
-
NullPointerException
- 如果newType
是空引用 -
WrongMethodTypeException
- 如果无法进行转换 - 另请参见:
-
MethodHandles.explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)
-
asSpreader
public MethodHandle asSpreader(类<?> arrayType, int arrayLength)
创建一个数组传播方法句柄,它接受一个尾随数组参数并将其元素作为位置参数传播。 新方法句柄将当前方法句柄作为其目标进行调整。 适配器的类型将与目标的类型相同,除了目标类型的最终arrayLength
参数被arrayLength
类型的单个数组参数arrayType
。如果数组元素类型与原始目标上的任何相应参数类型不同,则原始目标将适合直接获取数组元素,就像调用
asType
一样 。调用时,适配器用数组的元素替换尾随数组参数,每个元素作为目标的自己的参数。 (保留参数的顺序。)通过转换和/或取消装箱将它们成对转换为目标的尾随参数的类型。 最后调用目标。 目标最终返回的内容由适配器返回。
在调用目标之前,适配器会验证数组是否包含足够的元素,以便为目标方法句柄提供正确的参数计数。 (当需要零元素时,数组也可以为null。)
调用适配器时,将查询提供的
array
参数的长度,如同array.length
或arraylength
字节码一样。 如果适配器接受零长度尾随数组参数,则提供的array
参数可以是零长度数组或null
; 否则,如果阵列是null
,则适配器将抛出NullPointerException
如果阵列没有正确数量的元素,则抛出IllegalArgumentException
。以下是数组传播方法句柄的一些简单示例:
MethodHandle equals = publicLookup() .findVirtual(String.class, "equals", methodType(boolean.class, Object.class)); assert( (boolean) equals.invokeExact("me", (Object)"me")); assert(!(boolean) equals.invokeExact("me", (Object)"thee")); // spread both arguments from a 2-array: MethodHandle eq2 = equals.asSpreader(Object[].class, 2); assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" })); assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" })); // try to spread from anything but a 2-array: for (int n = 0; n <= 10; n++) { Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]); try { assert((boolean) eq2.invokeExact(badArityArgs) && false); } catch (IllegalArgumentException ex) { } // OK } // spread both arguments from a String array: MethodHandle eq2s = equals.asSpreader(String[].class, 2); assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" })); assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" })); // spread second arguments from a 1-array: MethodHandle eq1 = equals.asSpreader(Object[].class, 1); assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" })); assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" })); // spread no arguments from a 0-array or null: MethodHandle eq0 = equals.asSpreader(Object[].class, 0); assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0])); assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null)); // asSpreader and asCollector are approximate inverses: for (int n = 0; n <= 2; n++) { for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) { MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n); assert( (boolean) equals2.invokeWithArguments("me", "me")); assert(!(boolean) equals2.invokeWithArguments("me", "thee")); } } MethodHandle caToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, char[].class)); assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray())); MethodHandle caString3 = caToString.asCollector(char[].class, 3); assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C')); MethodHandle caToString2 = caString3.asSpreader(char[].class, 2); assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
- 参数
-
arrayType
- 通常为Object[]
,从中提取扩展参数的数组参数的类型 -
arrayLength
- 从传入数组参数传播的参数数 - 结果
- 在调用原始方法句柄之前传递最终数组参数的新方法句柄
- 异常
-
NullPointerException
- 如果arrayType
是空引用 -
IllegalArgumentException
- 如果arrayType
不是数组类型,或者目标不具有至少arrayLength
参数类型,或者如果arrayLength
为负数,或者结果方法句柄的类型将具有 too many parameters -
WrongMethodTypeException
- 如果隐含的asType
呼叫失败 - 另请参见:
-
asCollector(java.lang.Class<?>, int)
-
asSpreader
public MethodHandle asSpreader(int spreadArgPos, 类<?> arrayType, int arrayLength)
创建一个数组传播方法句柄,它接受给定位置的数组参数,并将其元素作为位置参数传播,代替数组。 新方法句柄将当前方法句柄作为其目标进行调整。 适配器的类型将与目标类型相同,除了从零开始位置spreadArgPos
开始的目标类型的arrayLength
参数被arrayLength
类型的单个数组参数arrayType
。此方法的行为与
asSpreader(Class, int)
非常相似,但接受另外的spreadArgPos
参数以指示参数列表中的哪个位置应进行扩散。- API Note:
-
例:
MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class)); MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2); Object[] ints = new Object[]{3, 9, 7, 7}; Comparator<Integer> cmp = (a, b) -> a - b; assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
- 参数
-
spreadArgPos
- 参数列表中应开始传播的位置(从零开始的索引)。 -
arrayType
- 通常为Object[]
,从中提取扩展参数的数组参数的类型 -
arrayLength
- 从传入数组参数传播的参数数 - 结果
- 在调用原始方法句柄之前,在给定位置传播数组参数的新方法句柄
- 异常
-
NullPointerException
- 如果arrayType
是空引用 -
IllegalArgumentException
- 如果arrayType
不是数组类型,或者目标不具有至少arrayLength
参数类型,或者arrayLength
为负数,或者spreadArgPos
具有非法值(否定,或者与arrayLength一起超过参数数量),或者如果结果方法句柄的类型将具有 too many parameters -
WrongMethodTypeException
- 如果隐含的asType
呼叫失败 - 从以下版本开始:
- 9
- 另请参见:
-
asSpreader(Class, int)
-
withVarargs
public MethodHandle withVarargs(boolean makeVarargs)
如果布尔标志为真,则将此方法句柄调整为variable arity ,否则为fixed arity 。 如果方法句柄已经是正确的arity模式,则返回不变。- API Note:
-
当调整可能是变量arity的方法句柄时,此方法有时很有用,以确保当且仅当原始句柄是时,生成的适配器也是可变arity。 例如,此代码将句柄
mh
的第一个参数更改为int
而不会干扰其变量arity属性:mh.asType(mh.type().changeParameterType(0,int.class)) .withVarargs(mh.isVarargsCollector())
此调用大致相当于以下代码:
if (makeVarargs == isVarargsCollector()) return this; else if (makeVarargs) return asVarargsCollector(type().lastParameterType()); else return return asFixedArity();
- 参数
-
makeVarargs
- 如果返回方法句柄应具有可变的arity行为,makeVarargs
true - 结果
- 一个相同类型的方法句柄,可能调整了变量arity行为
- 异常
-
IllegalArgumentException
- 如果makeVarargs
为true且此方法句柄没有尾随数组参数 - 从以下版本开始:
- 9
- 另请参见:
-
asVarargsCollector(java.lang.Class<?>)
,asFixedArity()
-
asCollector
public MethodHandle asCollector(类<?> arrayType, int arrayLength)
创建一个数组收集方法句柄,它接受给定数量的尾随位置参数并将它们收集到数组参数中。 新方法句柄将当前方法句柄作为其目标进行调整。 适配器的类型将与目标的类型相同,除了单个尾随参数(通常为arrayType
类型)由arrayLength
参数替换,其类型为元素类型arrayType
。如果数组类型与原始目标上的最终参数类型不同,则原始目标将适合直接获取数组类型,就像调用
asType
一样 。当被调用时,该适配器替换其尾随
arrayLength
由类型的单个新数组参数arrayType
,其元素包括(按顺序)被替换的参数。 最后调用目标。 目标最终返回的内容由适配器返回。(当
arrayLength
为零时,该数组也可以是共享常量。)( 注意:
arrayType
通常与原始目标的last parameter type相同。它是asSpreader
对称性的显式参数,并且还允许目标使用简单的Object
作为其最后一个参数类型。)要创建不限于特定数量的已收集参数的收集适配器,请改用
asVarargsCollector
或withVarargs
。以下是数组收集方法句柄的一些示例:
MethodHandle deepToString = publicLookup() .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"})); MethodHandle ts1 = deepToString.asCollector(Object[].class, 1); assertEquals(methodType(String.class, Object.class), ts1.type()); //assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"})); // arrayType can be a subtype of Object[] MethodHandle ts2 = deepToString.asCollector(String[].class, 2); assertEquals(methodType(String.class, String.class, String.class), ts2.type()); assertEquals("[two, too]", (String) ts2.invokeExact("two", "too")); MethodHandle ts0 = deepToString.asCollector(Object[].class, 0); assertEquals("[]", (String) ts0.invokeExact()); // collectors can be nested, Lisp-style MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2); assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D"))); // arrayType can be any primitive array type MethodHandle bytesToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class)) .asCollector(byte[].class, 3); assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3)); MethodHandle longsToString = publicLookup() .findStatic(Arrays.class, "toString", methodType(String.class, long[].class)) .asCollector(long[].class, 1); assertEquals("[123]", (String) longsToString.invokeExact((long)123));
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
arrayType
- 通常为Object[]
,将收集参数的数组参数的类型 -
arrayLength
- 要收集到新数组参数中的参数数 - 结果
- 一个新的方法句柄,它在调用原始方法句柄之前将一些尾随参数收集到一个数组中
- 异常
-
NullPointerException
- 如果arrayType
是空引用 -
IllegalArgumentException
- 如果arrayType
不是数组类型或arrayType
不能分配给此方法句柄的尾随参数类型,或者arrayLength
不是合法的数组大小,或者生成的方法句柄的类型将具有 too many parameters -
WrongMethodTypeException
- 如果隐含的asType
呼叫失败 - 另请参见:
-
asSpreader(java.lang.Class<?>, int)
,asVarargsCollector(java.lang.Class<?>)
-
asCollector
public MethodHandle asCollector(int collectArgPos, 类<?> arrayType, int arrayLength)
创建一个数组收集方法句柄,它接受从给定位置开始的给定数量的位置参数,并将它们收集到数组参数中。 新方法句柄将当前方法句柄作为其目标进行调整。 适配器的类型将与目标的类型相同,除了collectArgPos
(通常为arrayType
类型)指示的位置处的参数被arrayLength
参数替换,其类型为元素类型arrayType
。此方法的行为与
asCollector(Class, int)
非常相似,但不同之处在于其collectArgPos
参数指示应在参数列表参数中的哪个位置进行收集。 该指数从零开始。- API Note:
-
例子:
StringWriter swr = new StringWriter(); MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr); MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4); swWrite4.invoke('A', 'B', 'C', 'D', 1, 2); assertEquals("BC", swr.toString()); swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4); assertEquals("BCPQRS", swr.toString()); swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1); assertEquals("BCPQRSZ", swr.toString());
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
collectArgPos
- 参数列表中从零开始的位置,开始收集。 -
arrayType
- 通常为Object[]
,将收集参数的数组参数的类型 -
arrayLength
- 要收集到新数组参数中的参数数 - 结果
- 一个新的方法句柄,它在调用原始方法句柄之前将一些参数收集到一个数组中
- 异常
-
NullPointerException
- 如果arrayType
是空引用 -
IllegalArgumentException
- 如果arrayType
不是数组类型或arrayType
不能分配给此方法句柄的数组参数类型,或者arrayLength
不是合法的数组大小,或者collectArgPos
具有非法值(负数或大于参数数),或者生成的方法句柄的类型将具有 too many parameters -
WrongMethodTypeException
- 如果隐含的asType
调用失败 - 从以下版本开始:
- 9
- 另请参见:
-
asCollector(Class, int)
-
asVarargsCollector
public MethodHandle asVarargsCollector(类<?> arrayType)
使变量arity适配器能够接受任意数量的尾随位置参数并将它们收集到数组参数中。适配器的类型和行为将与目标的类型和行为相同,除了某些
invoke
和asType
请求可能导致将尾随位置参数收集到目标的尾随参数中。 此外,适配器的last parameter type将为arrayType
,即使目标具有不同的最后一个参数类型。这种转化可以返回
this
如果该方法手柄是可变的元数的已和它的尾部参数类型是相同的arrayType
。使用
invokeExact
调用时,适配器将调用目标而不更改参数。 ( 注意:此行为与fixed arity collector不同,因为它接受整个不确定长度的数组,而不是固定数量的参数。)当使用普通的,不精确的
invoke
调用时,如果调用者类型与适配器相同,则适配器将像invokeExact
一样调用目标。 (当类型匹配时,这是invoke
的正常行为。)否则,如果调用者和适配器arity相同,并且调用者的尾随参数类型是与适配器的尾随参数类型相同或可分配的引用类型,则参数和返回值将成对转换,就像
asType
一样在一个固定的arity方法句柄上。否则,arities会有所不同,或者适配器的尾随参数类型不能从相应的调用者类型中分配。 在这种情况下,适配器通过
arrayType
类型的新数组替换原始尾随参数位置的所有尾随参数,其元素包含(按顺序)替换的参数。调用者类型必须提供足够少的参数和正确的类型,以满足目标对尾随数组参数之前的位置参数的要求。 因此,调用者必须至少提供
N-1
参数,其中N
是目标的arity。 此外,必须存在从传入参数到目标参数的转换。 与普通invoke
其他用途invoke
,如果未满足这些基本要求,WrongMethodTypeException
可能会抛出WrongMethodTypeException
。在所有情况下,适配器都会返回目标最终返回的内容。
在最后一种情况下,就好像目标方法句柄暂时适应了调用者类型所需的arity fixed arity collector 。 (与
asCollector
,如果数组长度为零,则可以使用共享常量而不是新数组。如果对asCollector
的隐含调用将抛出IllegalArgumentException
或WrongMethodTypeException
,则对变量arity适配器的调用必须抛出WrongMethodTypeException
)的行为
asType
还专门为可变参数数量的适配器,保持不变的是普通的,不精确invoke
总是相当于asType
呼叫调整目标类型,其次为invokeExact
。 因此,当且仅当适配器和请求的类型在arity或trailing参数类型中不同时,变量arity适配器通过构建固定的arity收集器来响应asType
请求。 得到的固定arity收集器的类型通过成对转换进一步调整(如果需要)到所请求的类型,就像asType
的另一个应用程序asType
。当通过执行
CONSTANT_MethodHandle
常量的ldc
指令获得方法句柄,并且目标方法被标记为变量arity方法(使用修饰符位0x0080
)时,方法句柄将接受多个arities,就好像方法句柄常量是通过致电asVarargsCollector
。为了创建收集预定数量的参数的收集适配器,并且其类型反映该预定数量,请改用
asCollector
。没有方法句柄转换会产生具有变量arity的新方法句柄,除非它们被记录为这样做。 因此,除了
asVarargsCollector
和withVarargs
,在所有方法MethodHandle
和MethodHandles
将返回一个方法手柄固定元数,除非他们被指定为返回原来的操作数(例如,案件asType
的方法手柄自身的类型)。在已经是变量arity的方法句柄上调用
asVarargsCollector
将生成具有相同类型和行为的方法句柄。 它可能(或可能不)返回原始变量arity方法句柄。这是一个列表制作变量arity方法句柄的示例:
MethodHandle deepToString = publicLookup() .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class)); MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class); assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"})); assertEquals("[won]", (String) ts1.invoke( "won" )); assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"})); // findStatic of Arrays.asList(...) produces a variable arity method handle: MethodHandle asList = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); assertEquals(methodType(List.class, Object[].class), asList.type()); assert(asList.isVarargsCollector()); assertEquals("[]", asList.invoke().toString()); assertEquals("[1]", asList.invoke(1).toString()); assertEquals("[two, too]", asList.invoke("two", "too").toString()); String[] argv = { "three", "thee", "tee" }; assertEquals("[three, thee, tee]", asList.invoke(argv).toString()); assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString()); List ls = (List) asList.invoke((Object)argv); assertEquals(1, ls.size()); assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
讨论:这些规则被设计为变量arity方法的Java规则的动态类型变体。 在这两种情况下,可变arity方法或方法句柄的调用者可以传递零个或多个位置参数,或者传递任何长度的预收集数组。 用户应该知道最终参数的特殊作用,以及类型匹配对最终参数的影响,它确定单个尾随参数是否被解释为整个数组或数组的单个元素集。 请注意,尾随参数的动态类型对此决定没有影响,只是调用站点的符号类型描述符和方法句柄的类型描述符之间的比较。)
- 参数
-
arrayType
- 通常为Object[]
,将收集参数的数组参数的类型 - 结果
- 一个新的方法句柄,它可以在调用原始方法句柄之前将任意数量的尾随参数收集到一个数组中
- 异常
-
NullPointerException
- 如果arrayType
是空引用 -
IllegalArgumentException
- 如果arrayType
不是数组类型或arrayType
无法分配给此方法句柄的尾随参数类型 - 另请参见:
-
asCollector(java.lang.Class<?>, int)
,isVarargsCollector()
,withVarargs(boolean)
,asFixedArity()
-
isVarargsCollector
public boolean isVarargsCollector()
确定此方法句柄是否支持variable arity调用。 此类方法句柄来自以下来源:- 致电asVarargsCollector
- 调用lookup method ,它解析为变量arity Java方法或构造函数
- 一个
ldc
一个的指令CONSTANT_MethodHandle
其解析为一个可变参数数量的Java方法或构造
- 结果
-
如果此方法句柄接受多个简单,不精确的
invoke
调用,则invoke
- 另请参见:
-
asVarargsCollector(java.lang.Class<?>)
,asFixedArity()
-
asFixedArity
public MethodHandle asFixedArity()
创建一个固定的arity方法句柄,该句柄与当前方法句柄等效。如果当前方法句柄不是variable arity ,则返回当前方法句柄。 即使当前方法句柄不能是
asVarargsCollector
的有效输入,asVarargsCollector
。否则,生成的fixed-arity方法句柄与当前方法句柄具有相同的类型和行为,但
isVarargsCollector
将为false。 fixed-arity方法句柄可能(或可能不)是asVarargsCollector
的先前参数。这是一个列表制作变量arity方法句柄的示例:
MethodHandle asListVar = publicLookup() .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)) .asVarargsCollector(Object[].class); MethodHandle asListFix = asListVar.asFixedArity(); assertEquals("[1]", asListVar.invoke(1).toString()); Exception caught = null; try { asListFix.invoke((Object)1); } catch (Exception ex) { caught = ex; } assert(caught instanceof ClassCastException); assertEquals("[two, too]", asListVar.invoke("two", "too").toString()); try { asListFix.invoke("two", "too"); } catch (Exception ex) { caught = ex; } assert(caught instanceof WrongMethodTypeException); Object[] argv = { "three", "thee", "tee" }; assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString()); assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString()); assertEquals(1, ((List) asListVar.invoke((Object)argv)).size()); assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
- 结果
- 一个新方法句柄,只接受固定数量的参数
- 另请参见:
-
asVarargsCollector(java.lang.Class<?>)
,isVarargsCollector()
,withVarargs(boolean)
-
bindTo
public MethodHandle bindTo(Object x)
将值x
绑定到方法句柄的第一个参数,而不调用它。 新方法句柄通过将当前方法句柄绑定到给定参数来将其作为其目标进行调整。 绑定句柄的类型将与目标的类型相同,除了将省略单个前导引用参数。调用时,绑定句柄将给定值
x
作为目标的新前导参数插入。 其他参数也保持不变。 目标最终返回的内容由绑定句柄返回。参考
x
必须可转换为目标的第一个参数类型。注意:因为方法句柄是不可变的,所以目标方法句柄保留其原始类型和行为。
注意:生成的适配器永远不会是variable-arity method handle ,即使原始目标方法句柄是。
- 参数
-
x
- 要绑定到目标的第一个参数的值 - 结果
- 一个新的方法句柄,它在调用原始方法句柄之前将给定值预先添加到传入的参数列表中
- 异常
-
IllegalArgumentException
- 如果目标没有作为引用类型的前导参数类型 -
ClassCastException
- 如果x
无法转换为目标的前导参数类型 - 另请参见:
-
MethodHandles.insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...)
-
-