获取方法句柄

获取方法句柄和反射API类型,从已有的类中根据一定的条件进行查找,但与反射不同的是方法句柄部分构造器、方法和字段,而是统一转换成MethodHandle对象。对于字段类说,获取到的是用来获取和设置该字段值的方法句柄。

方法句柄的查找是通过 java.lang.invoke.MethodHandles.Lookup类来完成的。首先调用MethodHandles.lookup方法获取MethodHandles.Lookup对象,.MethodHandles.Lookup类提供了一些方法根据不同的条件进行查找:



//分别是字段Get和Set方法句柄,第一个参数为类的class对象,第二个参数为字段名,第三个参数为字段数据类型;
lookup.findGetter(C.class,"f",FT.class)
lookup.findSetter(C.class,"f",FT.class)
//分别是静态字段Get和Set方法句柄,第一个参数为类的class对象,第二个参数为字段名,第三个参数为字段数据类型;
lookup.findStaticGetter(C.class,"f",FT.class)
lookup.findStaticSetter(C.class,"f",FT.class)
//分别是一般方法和静态方法的方法句柄,第一个参数为类的class对象,第二个参数为方法名,第三个参数为MethodType对象;
lookup.findVirtual(C.class,"m",MT)
lookup.findStatic(C.class,"m",MT)
//构造器的方法句柄,第一个参数为类的class对象,第二个为MethodType对象;
lookup.findConstructor(C.class,MT)
//查找类中的特殊方法,主要是类中的私有方法。
lookup.findSpecial(C.class,"m",MT,this.class)



其中findSpecial方法比之前的findVirtual和findStatic等方法多了一个参数。这个额外的参数用来指定私有方法被调用时所使用的类。提供这个类的原因是为了满足对私有方法的访问控制的要求。当方法句柄被调用时,指定的调用类必须具备访问私有方法的权限,否则会出现无法访问的错误。

除了直接在某个类中进行查找之外,还可以从通过反射API得到的Constructor、Field和Method等对象中获得方法句柄。如代码清单2-47所示,首先通过反射API得到表示构造方法的Constructor对象,再通过unreflectConstructor方法就可以得到其对应的一个方法句柄;而通过unreflect方法可以将Method类对象转换成方法句柄。对于私有方法,则需要使用unreflectSpecial来进行转换,同样也需要提供一个作用与findSpecial中参数相同的额外参数;对于Field类的对象来说,通过unreflectGetter和unreflectSetter就可以得到获取和设置其值的方法句柄。请读者自行学习。

方法句柄的调用

在获取一个方法句柄后,最直接的使用方法就是调用它所引用的底层方法。比较常用的方法是invokeExact:

Object invokeExact(Object... args)

invokeExact的参数是可变的,参数一次是作为方法接受者的对象和调用时候的实际参数列表。但如果是静态方法,则不要指定接收对象,参数都是实际参数列表。



package net.oseye;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class ReflectTest {
	
	public static void main(String[] args) throws Throwable{ 
		User user=new User("kevin");
		MethodHandles.Lookup lookkup=MethodHandles.lookup();
		MethodHandle mth=lookkup.findVirtual(User.class, "SayHello", MethodType.methodType(void.class,String.class));
		mth.invokeExact(user,"oseye");
	}
}
class User{
	private String name;
	public User(String name){this.name=name;}
	public void SayHello(String name){
		System.out.println(this.name+" Say Hello,"+name);
	}
}