Java7新特性9-方法句柄

方法句柄-现代化的反射

Java7新特性9-方法句柄_第1张图片
1f2f7374e1990b4bb551fd141bc4a53d

java7中为间接调用方法引入了新的api,其中最关键的是java.lang.invoke包,即方法句柄。我们可以看成是java反射的升级版,比反射更加灵活、高效。

方法句柄中首先涉及到两个重要的类,MethodHandle和MethodType。

MethodHandle

它是对可直接执行的方法的类型的引用,或者说,它是一个有能力安全调用方法的对象。

通过句柄我们可以直接调用该句柄所引用的底层方法。从作用上来看,方法句柄类似于反射中的Method类,但是方法句柄的功能更加强大、使用更加灵活、性能也更好。

MethodType

它是表示方法签名类型的不可变对象。

每个方法句柄都有一个MethodType实例,用来指明方法的返回类型和参数类型。

它完全由参数类型和方法返回类型来确定,而与它所引用的底层的方法的名称和所在的类没有关系。

举个例子,例如String类的length方法和Integer类的intValue方法的方法句柄的类型就是一样的,因为这两个方法都没有参数,而且返回值类型都是int,则我们可以通过下列语句获取同一个方法类型:

MethodType mt =MethodType.methodType(int.class);

MethodType的对象实例只能通过MethodType类中的静态工厂方法来创建,参数是一个类型class的动态参数,按照返回值、参数的顺序。

如果没有参数可以只输入返回值类型的class。

MethodType.methodType(String.class);

表示方法的返回值是String,无参数。

使用示例

package java7;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

/**
 * @author qiang.xie
 * @date 2017/4/14
 */
public class MethodHandlerTest {

    public static void main(String[] arg) throws Throwable{
        //获取方法f1的methodType对象,表示此方法的返回值类型和参数类型
        MethodType f1=MethodType.methodType(String.class,int.class);
        MethodType f2=MethodType.methodType(void.class);//无参数,返回值是void的方法
        MethodType f3=MethodType.methodType(void.class);

        //通过MethodHandles.lookup()可以在一个类上根据方法名称和方法的methodType获取方法句柄
        //查找普通方法
        MethodHandle mf1=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f1",f1);
        MethodHandle mf2=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f2",f2);

        //查找静态方法
        MethodHandle mf3=MethodHandles.lookup().findStatic(MethodHandlerTest.class,"f3",f3);
        //通过方法句柄调用方法
        MethodHandlerTest methodHandler=new MethodHandlerTest();
        mf1.invoke(methodHandler,123);
        mf2.invoke(methodHandler);

        //使用invokeExact调用时,参数类型和返回值类型必须与方法签名的一致
        String v=(String)mf1.invokeExact(methodHandler,1234);

        //调用静态方法
        mf3.invoke();
    }


    public String f1(int a){
        System.out.println("f1"+a);
        return a+"";
    }

    public void f2(){
        System.out.println("f2");
    }

    public static void f3(){
        System.out.println("f3");
    }
}

运行结果:

f1123
f2
f11234
f3

示例解析

首先,在获取方法句柄之前,先通过MethodType的静态工厂方法,先生成一个包含方法返回类型、方法参数类型的方法类型。

其次,获取方法句柄要用到Lookup对象,这个对象可以提供其所在环境中任何可见方法的方法句柄。

我们可以将其比喻成包含有某个类对象的方法成员、方法的容器,通过

lookup.findVirtual(MethodHandlerTest.class,"f1",mt);

查找MethodHandlerTest类型中的f1方法,作为方法句柄返回。

要从lookup对象中得到方法句柄,需要给出持有所需方法的类,方法的名称,以及跟方法相匹配的方法类型。

最后,获取到方法句柄后,我们就可以通过方法句柄来调用底层的方法,这点上,跟反射中的方法调用类似。

方法句柄提供两个方法调用底层方法,invoke和invokeExact方法。

invokeExact方法在调用时要求严格的类型匹配,方法的返回值类型也在考虑范围之内,只要类型不能匹配就会报错。

而invoke允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作,如果不能正常转换才会报错

方法的调用参数基本都一样,第一个参数为方法的接受对象,即是哪个对象执行这个方法,接下来的参数就是执行方法所需要的参数。

这里需要强调一下,静态方法和动态方法之间的差别,静态方法是不需要指定方法的接受对象的,而一般方法是需要的。

@不迷失|知识改善生活

weixin

微信公众号:java技术

专注技术研究与视频教学,分享有价值的技术与经验,关注程序员的发展!

--
技术博客:http://bumishi.cn

技术交流群:245130488

@不迷失教学视频

QQ课堂:http://bumishi.ke.qq.com

百度传课:http://chuanke.com/s3377987.html

你可能感兴趣的:(Java7新特性9-方法句柄)