【Java基础】反射-invoke包

7 篇文章 0 订阅

什么是MethodHandle?

从Java 7开始提供了另一套API MethodHandle 。其与反射的作用类似,可以在运行时访问类型信息,但是据说其执行效率比反射更高,也被称为Java的 现代化反射。

官方对其定义如下:

A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values.

如何通过进行反射?

通过方法句柄来访问一下某个类里面的方法以及属性等,总的来说只需要4步:

  1. 创建Lookup
  2. 创建MethodType
  3. 基于Lookup与MethodType获得MethodHandle
  4. 调用MethodHandle

创建一个反射的目标类用于后面的例子

public class Hello {

    private String name;

    public void hello(String name){
        System.out.println("hello "+name);
    }

    public void say(String name){
        System.out.println("say "+name);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}
  1. 创建Lookup

    MethodHandles是MethodHandle的工厂类,它提供了一系列静态方法用于获取MethodHandle。使用如下代码创建一个lookup,以这种方式得到的lookup很强大,凡是调用类支持的字节码操作,它都支持。

    MethodHandles.Lookup lookup = MethodHandles.lookup();
    

    还可以使用publicLookup方法创建,但是以此种方式创建的lookup只能访问类中public的成员。

    MethodHandles.Lookup publicLookup=MethodHandles.publicLookup();
    
  2. 创建MethodType

    第一个参数是方法的返回类型,第二参数开始为方法的形式参数类型(例子中只有一个String类型参数,实际可以有多个)

    MethodType.methodType(Void.class, String.class)
    
  3. 基于Lookup与MethodType创建MethodHandle

    MethodHandle helloMethod = lookup.findVirtual(Hello.class, "hello", MethodType.methodType(Void.class, String.class));
    
  4. 通过MethodHandle传入对象实例targetInstance和方法参数字符串“MethodHandle”调用对象方法hello

    Hello targetInstance = new Hello();
    helloMethodHandle.invoke(helloInstance,"MethodHandle");
    
  5. 通过MethodHandle传入对象实例helloInstance和属性名称获取属性值

    //访问属性,需要借助反射得到Field,如果改属性是private的,还需要设置Field的setAccessible为true
    Field nameField = Hello.class.getDeclaredField("name");
    nameField.setAccessible(true);
    MethodHandle nameHandle = lookup.unreflectGetter(nameField);
    System.out.println((String) nameHandle.invoke(targetInstance));
    
    

反射API

创建MethodHandle

主要通过lookup里面的以下方法来创建MethodHandle

创建构造函数MethodHandle

public MethodHandle findConstructor(Class<?> refc, MethodType type) 

refc: 要检索的类
type: 对应的构造函数的MethodType

创建实例方法MethodHandle

public MethodHandle findVirtual(Class<?> refc, String name, MethodType type)

name: 方法名称

创建类方法的MethodHandle

public   MethodHandle findStatic(Class<?> refc, String name, MethodType type)

创建非private的Field的访问MethodHandle 。注意这个不是获取field的javabean Setter方法,与其毫无关系。通过这个setter 方法句柄我们就可以访问到这个属性了。

public MethodHandle findGetter(Class<?> refc, String name, Class<?> type)

对应的如果要设置此属性的值,使用Setter方法句柄

public MethodHandle findSetter(Class<?> refc, String name, Class<?> type)

通过MethodHandle调用目标方法

public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;

结语

  1. Java 9提供了增强的VarHandle,VarHandle主要用于动态操作数组的元素或对象的成员变量。VarHandle与MethodHandle非常类似,它也需要通过MethodHandles来获取实例,接下来调用VarHandle的方法即可动态操作指定数组的元素或指定对象的成员变量。
  2. 上文可知MethodHandle(方法句柄)访问目标对象属性值的时候需要借助反射的API。回忆一下Java反射一文中的结语,“但如果在Java 9或更高的版本中使用这种方式会看见这样的警告。而Java 16开始默认拒绝非法访问,于是会直接报错”。所以refection和invoke包的这两种方式都不是完全反射,java还提供了一种完全反射的方法,即通过UnSafe,下文详解。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值