方法句柄
方法句柄(method handle)是JSR 292中引入的一个重要概念,它是对Java中方法、构造方法和域的一个强类型的可执行的引用。这也是句柄这个词的含义所在。通过方法句柄可以直接调用该句柄所引用的底层方法。从作用上来说,方法句柄的作用类似于2.2节中提到的反射API中的Method类,但是方法句柄的功能更强大、使用更灵活、性能也更好。实际上,方法句柄和反射API也是可以协同使用的,下面会具体介绍。
在Java标准库中,方法句柄是由java.lang.invoke.MethodHandle类来表示的。
1.方法句柄的类型
对于一个方法句柄来说,它的类型完全由它的参数类型和返回值类型来确定,而与它所引用的底层方法的名称和所在的类没有关系。比如引用String类的length方法和Integer类的intValue方法的方法句柄的类型就是一样的,因为这两个方法都没有参数,而且返回值类型都是int。
在得到一个方法句柄,即MethodHandle类的对象之后,可以通过其type方法来查看其类型。该方法的返回值是一个java.lang.invoke.MethodType类的对象。MethodType类的所有对象实例都是不可变的,类似于String类。所有对MethodType类对象的修改,都会产生一个新的MethodType类对象。两个MethodType类对象是否相等,只取决于它们所包含的参数类型和返回值类型是否完全一致。
1.1MethodType类的对象实例的创建
MethodType类的对象实例只能通过MethodType类中的静态工厂方法来创建。这样的工厂方法有三类。
1.1.1 通过指定参数和返回值的类型来创建MethodType.【显式地指定返回值和参数的类型】
这主要是使用methodType方法的多种重载形式。使用这些方法的时候,至少需要指定返回值类型,而参数类型则可以是0到多个。
返回值类型总是出现在methodType方法参数列表的第一个,后面紧接着的是0到多个参数的类型。类型都是由Class类的对象来指定的。如果返回值类型是void,可以用void.class或java.lang.Void.class来声明。
代码清单2-31中给出了使用methodType方法的几个示例。注意:最后一个methodType方法调用中使用了另外一个MethodType的参数类型作为当前MethodType类对象的参数类型。
代码清单2-31 MethodType类中的methodType方法的使用示例
public void generateMethodTypes(){
//String.length()
MethodType mt1=MethodType.methodType(int.class);
//String.concat(String str)
MethodType mt2=MethodType.methodType(String.class, String.class);
//String.getChars(int srcBegin, int srcEnd, char[]dst, int dstBegin)
MethodType mt3=MethodType.methodType(void.class, int.class, int.class, char[].class, int.class);
//String.startsWith(String prefix)
MethodType mt4=MethodType.methodType(boolean.class, mt2);
}
1.1.2 通过静态工厂方法genericMethodType来创建的
除了显式地指定返回值和参数的类型之外,还可以生成通用的MethodType类型,即返回值和所有参数的类型都是Object类。
方法genericMethodType有两种重载形式:
第一种形式只需要指明方法类型中包含的Object类型的参数个数即可。
第二种形式可以提供一个额外的参数来说明是否在参数列表的后面添加一个Object[]类型的参数。
在代码清单2-32中,mt1有3个类型为Object的参数,而mt2有2个类型为Object的参数和后面的Object[]类型参数。
代码清单2-32 生成通用MethodType类型的示例
public void generateGenericMethodTypes(){
MethodType mt1=MethodType.genericMethodType(3);
MethodType mt2=MethodType.genericMethodType(2,true);
}
1.1.2 通过静态工厂方法fromMethodDescriptorString来创建的
最后介绍的一个工厂方法是比较复杂的fromMethodDescriptorString。这个方法允许开发人员指定方法类型在字节代码中的表示形式作为创建MethodType时的参数。这个方法的复杂之处在于字节代码中的方法类型格式不是很好理解。
比如代码清单2-31中的String.getChars方法的类型在字节代码中的表示形式是“(II[CI)V”。不过这种格式比逐个声明返回值和参数类型的做法更加简洁,适合于对Java字节代码格式比较熟悉的开发人员。
在代码清单2-33中,“(Ljava/lang/String;)Ljava/lang/String;”所表示的方法类型是返回值和参数类型都是java.lang.String,相当于使用MethodType.methodType(String.class, String.class)。
代码清单2-33 使用方法类型在字节代码中的表示形式来创建MethodType
public void generateMethodTypesFromDescriptor(){
ClassLoader cl=this.getClass().getClassLoader();
String descriptor="(Ljava/lang/String;)Ljava/lang/String;";
MethodType mt1=MethodType.fromMethodDescriptorString(descriptor, cl);
}
注意:在使用fromMethodDescriptorString方法的时候,需要指定一个类加载器。该类加载器用来加载方法类型表达式中出现的Java类。如果不指定,默认使用系统类加载器。
2 对MethodType类的对象实例的修改
2.1 围绕返回值和参数类型的精确修改
在通过工厂方法创建出MethodType类的对象实例