public abstract class MethodHandle extends Object
每一种方法处理报告类型说明符通过type
访问。这种类型的描述符是一个MethodType
对象,其结构是一系列课程,其中一个方法的返回类型(或void.class
如果没有)。
一种方法处理的类型的控件调用它接受的类型,和转换,适用于它的种类。
一种方法处理包含一对特殊的调用方法称为invokeExact
和invoke
。这两个调用方法提供直接访问的方法处理的基本方法、构造函数、字段,或其他操作,如修改转换参数和返回值。这两个调用接受完全匹配的方法处理自己的类型调用。平原,不精确的调用也接受一系列其他呼叫类型。
方法句柄是不可变的,并且没有可见的状态。当然,它们可以绑定到显示状态的基本方法或数据。关于java内存模型,任何方法处理表现得仿佛所有的(内部)领域的最终变量。这意味着,任何方法处理的应用程序的可见光将永远是完全形成的。这是真的,即使方法处理是通过数据竞争中的共享变量发布的。
方法处理不能由用户定义。实现可能(或不可能)创造MethodHandle
可通过操作内部的子类Object.getClass
可见。程序员不应该从它的特定类的方法处理的结论,作为方法处理类层次结构(如果有的话)可能会改变,从时间到时间或在不同的供应商的实现。
invokeExact
或
invoke
可以调用方法处理java源code。从源代码的角度来看,这些方法可以采取任何参数,它们的结果可以被转换为任何返回类型。正式这是给调用方法的返回类型和变量的数量
Object
Object
参数来完成的,但他们有一个额外的质量称为签名多态连接自由直接调用JVM执行堆栈。
通常是与虚拟方法,源代码级的调用invokeExact
和invoke
编译到一个invokevirtual
指令。更不寻常的是,编译器必须记录实际的参数类型,并可能不在参数上执行方法调用转换。相反,它必须在堆栈的根据自己的未转化的类型。方法句柄对象本身在参数之前被推到堆栈上。然后,编译器调用一个描述参数和返回类型的符号类型描述符的方法句柄。
要发出一个完整的符号类型描述符,编译器还必须确定返回类型。这是基于方法调用表达式转换,如果有一个,否则Object
如果调用一个表达式或其他void
如果调用语句。演员可能是原始类型(但不是void
)。
作为一个角落的情况下,一个uncasted null
给出的说法是一个象征性的java.lang.Void
类型说明符。同型Void
歧义是无害的,因为没有引用类型Void
除了空引用。
invokevirtual
指令执行是相连的,通过象征性地解决名称在指导和验证方法的调用是静态的法律。这是电话
invokeExact
和
invoke
真实。在这种情况下,编译器发出的符号类型描述符检查正确的语法和它包含的名称是否被解决。因此,一个
invokevirtual
指令调用一个方法处理将始终链接,只要符号类型说明符是合乎语法和类型存在。在典型的程序
当invokevirtual
后进行连接,接收方法处理的类型是第一次检查由JVM保证匹配的符号类型说明符。如果类型匹配失败,则意味着调用方调用的方法不在调用被调用的单个方法句柄上存在。
在invokeExact
的情况下,调用的类型说明符(解析符号类型名称后)必须接收方法处理方法类型完全匹配。在平原,如此精确invoke
,解决类型说明符必须是一个接收器的asType
方法有效性的争论。因此,平原invoke
比invokeExact
更加宽容。
经过配型,一个叫invokeExact
直接调用的方法处理的基本方法(或其他行为,视情况而定)。
一个叫平invoke
作品视为invokeExact
相同,如果符号类型说明符调用者指定的方法处理自己的类型完全匹配。如果类型不匹配,invoke
试图调整接收方法处理的类型,如电话asType
,获得准确的调用方法处理M2
。这允许一个更强大的谈判方法的调用者和被调用者之间。
(注:调整的方法处理M2
是不能直接观察的,因此不需要实现兑现。)
WrongMethodTypeException
,要么直接(在
invokeExact
的情况)或间接通过电话
asType
失败(在
invoke
案例)。
因此,一个方法的类型不匹配,也可能表现为在一个静态类型的程序联动误差可以表现为一个动态WrongMethodTypeException
程序中采用的方法处理。
因为方法类型包含“活”类
对象、方法类型匹配,考虑到这两种类型的名称和类装载器。因此,即使一个方法处理M
是一类装载器L1
创造使用的另一L2
,处理方法调用是类型安全的,因为调用者的符号类型描述,为解决L2
,是对原有方法的调用者符号类型说明符匹配,为解决L1
。在L1
决议时发生的M
创建及其类型的分配,而在L2
决议时发生的invokevirtual
指令链接。
除了检查类型描述符,一个方法处理的调用它的基本方法的能力是不受限制的。如果一个方法处理是由一个类,有访问该方法,非公开的方法,得到的处理可以使用在任何地方的任何人收到一个参考它。
与核心反射API,在访问检查,每一次反射的方法被调用时,处理方法是进行访问检查when the method handle is created。在ldc
案例(见下文),访问检查作为连接常量池入口的基本常数的方法部分进行处理。
因此,处理非公开的方法,或在非公开课的方法,一般应保密。他们不应该通过不受信任的代码使用,除非他们来自不受信任的代码都是无害的。
MethodHandles.Lookup
例如,静态方法处理可以从
Lookup.findStatic
。也有从核心反射API对象转换方法,如
Lookup.unreflect
。
如类和方法处理字符串,对应的可访问的字段,方法和构造函数也可以直接表示在类文件中的常量池常数被ldc
字节码加载。一种新的常量池入口,CONSTANT_MethodHandle
型,指的是直接相关的CONSTANT_Methodref
,CONSTANT_InterfaceMethodref
,或CONSTANT_Fieldref
常量池入口。(全部细节的方法处理常数,见第4.4.8和5.4.3.5的java虚拟机的规范。)
方法处理的方法或构造函数的变量数量修改位查找或恒载产生的(0x0080
)有一个相应的变量的数量,如果他们与asVarargsCollector
帮助定义。
一种方法引用可以参考静态或非静态方法。在非静态的情况下,该方法处理类型包括显式接收机的说法,加上之前的任何其他参数。在方法句柄的类型中,初始接收参数根据初始请求的类类型进行类型化。(例如,如果一个非静态方法处理是通过ldc
,接收器的类型是类中的常量池入口。命名)
方法处理常数是相同的链接访问检查相应的字节码指令,和ldc
指令将相应的联动误差如果字节码的行为会把这样的错误。
作为这一推论,访问受保护的成员仅限于访问类的接收器,或它的子类之一,而访问类必须是保护成员的定义类的一个子类(或包的兄弟姐妹)。如果方法引用是指在当前包外的一个类的受保护的非静态方法或字段,则接收参数将被缩小到访问类的类型。
当调用一个方法处理虚拟方法时,该方法总是在接收端(即第一个参数)被查到。
也可以创建一个非虚拟方法句柄到一个特定的虚拟方法实现。这些不执行基于接收器类型的虚拟查找。这种方法处理模拟的方法invokespecial
教学效果。
上述要求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
或平原
invoke
生成的符号类型说明符单虚调用指令在下面评论表示。在这些例子中,辅助方法
assertEquals
被假定为一个方法调用
Objects.equals
其论点,并断言的结果是真的。
invokeExact
和
invoke
宣布把
Throwable
,即无什么限制方法处理可以把。由于JVM不区分检查和未检查的异常(其他比他们班的,当然),没有特别的效果,对字节码的形状将检查异常处理方法调用。但在java源代码,执行方法中处理呼叫的方法必须明确把
Throwable
,否则必须抓住所有throwables局部,积极只有那些法律在这样的背景下,包裹的是非法的。
invokeExact
平原
invoke
联动行为是由术语签名多态引用。在java语言规范的定义,一个签名的多态性的方法,是一种能操作各种调用签名和返回类型。
在源代码中,调用一个签名多态方法将编译,无论所请求的符号类型描述符。像往常一样,java编译器会发出与给定的符号类型描述与命名方法invokevirtual
指令。不寻常的部分是,符号类型的描述符是来自实际的参数和返回类型,而不是从方法声明。
当JVM进程中包含签名多态调用的字节码,它将成功地连接任何这样的电话,不管它的符号类型说明符。(为了保留类型安全,JVM会保护这样的调用合适的动态类型检查,如所描述的地方。)
字节码发生器,包括编译器的后端,需要发出未转化的象征型描述这些方法。工具确定符号联动需要接受这样的未描述,没有报告链接错误。
Lookup
API,任何类成员的核心反射API对象表示可以转换为等效的方法处理行为。例如,一个反射
方法
可以转换为使用
Lookup.unreflect
方法处理。由此产生的方法处理一般提供更直接和有效的访问底层类成员。
作为一种特殊情况,当核心反射API用于这类视图签名多态方法invokeExact
或平原invoke
,它们的出现为普通非多态性的方法。反思的样子,看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
或平原invoke
,对于任何指定类型说明符。
invokevirtual
指示符号类型说明符。
方法处理不代表他们的功能就像在java参数类型(通用)类型,因为有三个错配的功能类型和参数化类型的java。
long
或double
参数计数(对数量限制)两参数插槽。invoke
方法(或其他签名多态方法)是非虚拟的,它消耗的方法的一个额外的参数处理本身,除了任何非虚拟接收对象。IllegalArgumentException
。特别是,一个方法处理的类型不能有确切的最高255元。
MethodType
,
MethodHandles
Modifier and Type | Method and Description |
---|---|
MethodHandle |
asCollector(类<?> arrayType, int arrayLength)
一个数组集合方法处理,它接受一个给定数量的尾随位置参数,并将它们收集到一个数组参数中。
|
MethodHandle |
asFixedArity()
使一个固定数量的方法处理,否则,相当于现在的处理方法。
|
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(List<?> arguments)
执行一个可变数量的调用,参数传递在给定的数组的方法处理,如果通过电话的网站,只提到的类型
Object 不精确的
invoke ,其数量是参数数组的长度。
|
Object |
invokeWithArguments(Object... arguments)
执行一个可变数量的调用,参数传递在给定列表中的方法处理,如果通过电话的网站,只提到的类型
Object 不精确的
invoke ,其数量是参数列表的长度。
|
boolean |
isVarargsCollector()
决定如果这方法处理
variable arity电话支持。
|
String |
toString()
返回一个字符串表示的方法处理,从字符串
"MethodHandle" 和结束与处理方法的类型的字符串表示形式。
|
MethodType |
type()
报告该方法句柄的类型。
|
public MethodType type()
invokeExact
必须匹配这种类型是。
public final Object invokeExact(Object... args) throws Throwable
invokeExact
调用点符号类型说明符必须符合本法处理的
type
准确。没有转换参数或返回值允许在。
当这个方法通过核心反射的接口被观察时,它将显示为一个单一的原生方法,以一个对象数组和返回一个对象。如果这个本地方法调用直接通过java.lang.reflect.Method.invoke
,通过JNI,或间接通过Lookup.unreflect
,它将抛出一个UnsupportedOperationException
。
args
-签名多态参数列表,静代表使用varargs
Object
WrongMethodTypeException
-如果目标的类型不相同来电者的符号类型说明符
Throwable
-任何基本的方法被传播不变的呼叫处理方法
public final Object invoke(Object... args) throws Throwable
如果调用网站的符号类型描述该方法处理的type
完全匹配,电话收益如invokeExact
。
否则,如果该方法调用进行处理首先是通过调用asType
调整调整方法处理所需的类型,然后调用过程如invokeExact
对调整的方法处理。
没有保证,asType
电话实际上是由。如果JVM可以预测进行调用的结果,它可以适应直接调用者的参数,并根据自己的确切类型调用目标方法处理。
解决类型说明符在invoke
呼叫网站必须有一个接收器asType
方法有效性的争论。特别是,调用者必须指定相同的参数数量是被调用者的类型,如果调用者不variable arity collector。
当这个方法通过核心反射的接口被观察时,它将显示为一个单一的原生方法,以一个对象数组和返回一个对象。如果这个本地方法调用直接通过java.lang.reflect.Method.invoke
,通过JNI,或间接通过Lookup.unreflect
,它将抛出一个UnsupportedOperationException
。
args
-签名多态参数列表,静代表使用varargs
Object
WrongMethodTypeException
-如果目标类型无法调整到调用者的符号类型说明符
ClassCastException
-如果目标类型可以调整来电,但引用转换失败
Throwable
-任何基本的方法被传播不变的呼叫处理方法
public Object invokeWithArguments(Object... arguments) throws Throwable
Object
不精确的
invoke
,其数量是参数列表的长度。
具体来说,继续执行,如下面的步骤,虽然方法不保证是如果JVM可以预测他们的影响。
N
长度为空引用,N=0
。N
参数的通用型TN
作为TN=MethodType.genericMethodType(N)
。MH0
所需要的类型,如MH1 = MH0.asType(TN)
。N
单独的参数A0, ...
。Object
参考。由于该asType
步动作,以下说法转换的应用是必要的:
调用调用返回的结果是装箱,如果它是一个原始的,或被迫为空,如果返回类型是无效的。
此调用相当于下面的代码:
MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0); Object result = invoker.invokeExact(this, arguments);
不像签名多态方法invokeExact
和invoke
,invokeWithArguments
可以正常通过核心反射API和JNI访问。因此,它可以被用作本地或反射代码和方法处理之间的桥梁。
arguments
-到目标参数
ClassCastException
如果争论不能铸造转换参考
WrongMethodTypeException
-如果目标类型无法调整以给定的参数个数
Object
Throwable
任何由目标方法调用抛出
MethodHandles.spreadInvoker(java.lang.invoke.MethodType, int)
public Object invokeWithArguments(List<?> arguments) throws Throwable
Object
不精确的
invoke
,其数量是参数数组的长度。
此方法也相当于下面的代码:
invokeWithArguments(arguments.toArray()
arguments
-到目标参数
NullPointerException
-如果
arguments
是空引用
ClassCastException
如果争论不能铸造转换参考
WrongMethodTypeException
-如果目标类型无法调整以给定的参数个数
Object
Throwable
任何由目标方法调用抛出
public MethodHandle asType(MethodType newType)
如果原始型和新型相等,返回this
。
当调用的新方法处理时,将执行以下步骤:
这种方法提供了invokeExact
和平原之间的行为差异,关键不invoke
。两方法执行相同的步骤时,来电者的类型说明符是M、被叫方的,但类型不同,平原invoke
也称asType
(或一些内部等效)为配合呼叫者和被叫方的类型。
如果当前方法的可变数量的方法处理的参数列表转换可能涉及的几个参数为数组的转换和采集,为described elsewhere。在每一个其他情况下,所有的转换都是成对的,这意味着每个参数或返回值被转换为一个参数或返回值(或不返回值)。通过咨询旧的和新的方法句柄类型的相应的组件类型来定义所应用的转换。
让T0和T1是相应的新的和旧的参数类型,或旧的和新的返回类型。具体来说,对于一些有效的指标i
,让t0=newType.parameterType(i)
和t1=this.type().parameterType(i)
。否则,将返回值的其他方式,让t0=this.type().returnType()
和t1=newType.returnType()
。如果类型是相同的,新方法处理将不会更改相应的参数或返回值(如果有的话)。否则,如果可能的话,应用以下的一个转换:
java.lang.reflect.Method.invoke
。转换)拆箱转换必须有一个成功的可能性,这意味着如果T0本身不是一个包装类,必须存在至少一个包装类TW是T0型和原始值的办法可以加宽T1。如果无法作出所需的成对转换的任何一个,将无法进行方法处理转换。
在运行时,应用到参考参数或返回值的转换可能需要额外的运行时检查,它可以失败。拆箱的操作可能会失败,因为原来的参考是无效的,造成NullPointerException
。拆箱操作或参投的也可以在参考一个错误类型的对象失败,造成ClassCastException
。虽然拆箱操作可以接受各种包装,如果没有,一个ClassCastException
将抛出。
newType
--新方法处理预期的类型
this
执行任何必要的参数转换后,并安排必要的返回值的转换
NullPointerException
-如果
newType
是空引用
WrongMethodTypeException
-如果转换不能
MethodHandles.explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)
public MethodHandle asSpreader(类<?> arrayType, int arrayLength)
arrayLength
参数由单一型
arrayType
数组参数替换。
如果数组元素的类型不同于任何在原目标对应的参数类型,最初的目标是适应以数组元素直接,如果被调用的asType
。
当调用时,适配器将数组元素的一个数组元素替换为一个数组参数,每个数组都作为它自己的参数到目标。(论据的顺序是一样的。)他们是通过铸造和/或拆箱的尾随参数目标的类型间转换。最后的目标被称为。目标最终返回的是由适配器保持不变的。
在调用目标之前,适配器验证数组中包含了足够多的元素,以提供正确的参数计数到目标方法句柄。(当需要零元素时,数组也可以是空的。)
如果,当是所谓的适配器,提供的数组参数没有元素的正确的数字,该适配器将抛出一个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 ? null : 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)
public MethodHandle asCollector(类<?> arrayType, int arrayLength)
arrayType
)是由
arrayLength
参数的类型是
arrayType
元素类型所取代。
如果数组类型不同于原来的最终目标参数类型,最初的目标是适应直接把数组类型,如电话asType
。
在调用时,适配器通过一个新的阵列式arrayType
取代其尾arrayLength
争论,其要素包括(按顺序)替换的参数。最后的目标被称为。目标最终返回的是由适配器保持不变的。
(数组也可以共享一个常数时arrayLength
是零。)
(注:该arrayType
经常对原有目标的最后一个参数类型相同。这是一个明确的说法asSpreader
对称,并允许目标使用一个简单的Object
作为最后一个参数的类型。)
为了创建一个收集适配器不限于特定数量的采集参数,而不是使用asVarargsCollector
。
这里有一些数组收集方法句柄的例子:
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));
arrayType
经常
Object[]
的数组参数,将收集的参数类型
arrayLength
-参数个数收集到一个新的数组参数
NullPointerException
-如果
arrayType
是空引用
IllegalArgumentException
-如果
arrayType
不是数组类型或
arrayType
不能分配给该方法处理后的参数类型,或
arrayLength
不是合法的数组大小,或产生的方法处理的类型会
too many parameters
WrongMethodTypeException
如果隐含
asType
调用失败
asSpreader(java.lang.Class<?>, int)
,
asVarargsCollector(java.lang.Class<?>)
public MethodHandle asVarargsCollector(类<?> arrayType)
该适配器的类型和行为将作为目标的类型和行为一样,除了一定的invoke
和asType
请求会导致尾位置参数被收集到目标的跟踪参数。同时,该适配器的最后一个参数类型将arrayType
,即使目标有不同的最后一个参数类型。
这种转变可能返回this
如果方法处理已经变元数及其尾随的参数类型是相同的arrayType
。
当使用invokeExact
,适配器调用目标没有参数的变化。(注:此行为不同于fixed arity collector,因为它接受了一系列不确定的长度,而不是一个固定数量的参数。)
当调用invoke
平原,不精确,如果调用方式为适配器一样,适配器调用的目标与invokeExact
。(这是invoke
正常时的行为类型相匹配。)
否则,如果调用者和适配器的数量是相同的,和后面的参数类型的调用是一个或转让给尾随的参数类型的适配器相同的引用类型的参数和返回值被转换成对,如在固定数量的方法处理asType
。
否则,受的不同,或适配器的后缀参数类型不可从相应的呼叫类型。在这种情况下,适配器替换所有尾参数从原始落后的论点的位置开始,以一个新的类型arrayType
数组,其元素包括(按顺序)替换的参数。
调用方类型必须提供足够多的参数,并提供正确的类型,以满足目标对在尾随数组参数之前的位置参数的要求。因此,调用方必须提供,至少,N-1
争论,在N
是目标的数量。此外,必须存在从传入的参数到目标的参数的转换。与普通invoke
其他用途,如果不能满足这些基本的要求,可能会引发一WrongMethodTypeException
。
在所有情况下,目标最终返回的是由适配器保持不变的。
在最后的情况下,正是因为如果目标方法处理暂时与fixed arity collector由调用类型所需的数量相适应。(如asCollector
,如果数组的长度是零,一个共享的常数可用来代替一个新的数组。如果asCollector
隐含调用会抛出一个IllegalArgumentException
或WrongMethodTypeException
,调用的变量数量必须把WrongMethodTypeException
。)
对asType
行为也专门为变元的适配器,维持不变,平原,不精确的invoke
总是相当于一个asType
叫调整目标类型,其次是invokeExact
。因此,一个变量的数量适配器响应建立一个固定数量的asType
收藏家的要求,当且仅当适配器和请求类型的不同在数量或尾随的参数类型。由此产生的固定性收集器类型进一步调整(如有必要)所要求的类型的配对转换,好像被另一个应用程序asType
。
当一个方法处理是通过执行一个CONSTANT_MethodHandle
常数的ldc
指令得到,和目标的方法标记为可变数量的方法(与改性剂点0x0080
),处理方法将接受多个元,如果处理方法常被调用asVarargsCollector
意味着创造。
为了创建一个收集适配器将预定数量的参数,其类型反映了这种预定数量,而不是使用asCollector
。
没有方法处理转换,产生新的方法处理变量的数量,除非他们有这样。因此,除了asVarargsCollector
,在MethodHandle
和MethodHandles
所有方法将返回一个固定数量的方法处理,除的情况下,他们被指定返回原来的操作(例如,asType
的方法处理自己的类型)。
在一个方法中处理已经变元的电话asVarargsCollector
会产生具有相同的类型和行为的处理方法。它可能(或不可能)返回原变量数量的方法处理。
这里有一个例子,一个单变量数量的方法处理:
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)));
讨论:这些规则被设计为一个动态变化的变量数量方法的java规则。在这两种情况下,调用者的变量数量的方法或方法处理可以通过零个或多个位置参数,或者通过预先收集的任何长度的数组。用户应该知道的最后一个参数的特殊作用,和一个类型匹配的效果上的最后一个参数,它决定是否一个单一的尾随参数被解释为一个完整的数组或一个数组的一个单一的元素被收集。请注意,尾随参数的动态类型对这个决定没有影响,只有调用站点的符号类型描述符和方法句柄的类型描述符之间的比较。
arrayType
经常
Object[]
的数组参数,将收集的参数类型
NullPointerException
-如果
arrayType
是空引用
IllegalArgumentException
-如果
arrayType
不是数组类型或
arrayType
不能分配给该方法的参数类型处理后
asCollector(java.lang.Class<?>, int)
,
isVarargsCollector()
,
asFixedArity()
public boolean isVarargsCollector()
CONSTANT_MethodHandle
解决一个变量数量java的方法或构造函数的ldc
指令invoke
电话
asVarargsCollector(java.lang.Class<?>)
,
asFixedArity()
public MethodHandle asFixedArity()
如果当前方法处理不variable arity,当前方法的句柄返回。即使目前的处理方法不能有效的输入,这是真的asVarargsCollector
。
否则,由此产生的固定性的方法处理当前的方法处理相同的类型和行为,除了isVarargsCollector
将假。固定数量的方法处理可能(或不可能)是以前的说法asVarargsCollector
。
这里有一个例子,一个单变量数量的方法处理:
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()
public MethodHandle bindTo(Object x)
x
的处理方法的第一个参数,而不调用它。新的方法处理相适应,为空目标,目前的方法处理绑定到给定的参数。绑定句柄的类型将与目标的类型相同,只不过将省略一个前导引用参数。
在调用时,绑定的手柄插入给定值x
作为一种新的主要论点的目标。其他参数也传递不变。目标最终返回的是由绑定句柄保持不变的。
参考x
必须转换为目标的第一个参数的类型。
(注:因为方法句柄是不可变的,目标方法句柄保留了它的原始类型和行为。)
x
-绑定到目标函数的第一个参数的值
IllegalArgumentException
-如果目标没有一个领先的参数类型是引用类型
ClassCastException
-如果
x
不能转换为目标类型的主要参数
MethodHandles.insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...)
Submit a bug or feature
For further API reference and developer documentation, see Java SE Documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.
Copyright © 1993, 2014, Oracle and/or its affiliates. All rights reserved.