OkReflect:Java 反射框架

作者: 灯不利多 | 来源:发表于2019-07-02 01:36 被阅读8次

    1. 前言

    1. 什么是 OkReflect?

    OkReflect 是一个对 Java 反射操作进行了封装的工具。

    2. 为什么我要开发 OkReflect?

    在 Android 组件化架构中,作者提到了 jOOR 反射框架,刚看到 jOOR 的时候,觉得使用 jOOR 和常规的反射操作比起来优雅很多。但是当我在不同分场景下对它进行进行一些测试时,却发现了一些问题,于是我就在想难道没有更好的解决方案了吗?于是我就写了 OkReflect。

    3. 什么是反射?

    Java 反射能在程序在运行时,对于任意一个类或对象,都获取和调用这个类或对象的的所有方法和属性。这好像听上去有点抽象,下面我们来看一些实例。

    1. 正常创建字符串

    String str = "666";
    

    2. 使用反射创建字符串

    try {
        Class clazz = String.class;
        Constructor constructor = clazz.getConstructor(String.class);
        String instance = (String) constructor.newInstance("666");
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    

    这么一看,反射也太麻烦了,有啥用?但如果你想创建的实例的构造函数是私有的,你调不到,这时候反射就派上用场了。

    4. 使用反射创建实例

    假设我们现在有一个 Client 类,它的构造函数和方法都是私有的,这时候我们通过反射就能创建这个类,并且调用它的私有方法。

    public class Client {
    
        private String name;
    
        private Client(String name) {
            this.name = name;
        }
      
        private void setName(String name) {
            this.name = name;
        }
    
    }
    
    try {
        Class clazz = Class.forName("Client");
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Client client = (Client) constructor.newInstance("小张");
        Method method = clazz.getDeclaredMethod("setName", String.class);
        method.setAccessible(true);
        method.invoke(client, "老王");
        System.out.println("");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    

    创建类和调用方法的效果是达到了,但是这代码看上去也太不美观了,别怕,有 jOOR。

    5. 什么是 jOOR?

    1. 介绍

    jOOR 是一个老牌的反射框架,对 Java 反射操作进行了封装,且底层用了性能比默认的反射操作高的方法句柄 MethodHandles。

    2. 用法

    String world = on("java.lang.String")  // 相当于 Class.forName()
                    .create("Hello World") // 调用构造函数创建实例
                    .call("substring", 6)  // 调用 substring 方法
                    .get();                // 获取方法返回结果
    

    异常都不见了,挺好,但你还真不能直接这样用,出问题了还是会报异常。jOOR 自定义了 ReflectException,反射中发生的异常会被封装成 ReflectException,所以你还是要包一层 try…catch。

    try {
        String world = on("java.lang.String")
                .create("Hello World")
                .call("substring", 6)
                .get();
    } catch (ReflectException e) {
        // 处理异常
    }
    

    虽然还是要包一层 try…catch,但是比最原始的反射操作是要好多了,但还有没有更好的办法呢?下面看 OkReflect 的处理。

    6. 使用 OkReflect 进行反射

    try {
        String world = OkReflect.on("java.lang.String")
                .create("Hello World")
                .call("substring", 6)
                .get();
    } catch (Exception e) {
        // 处理异常
    }
    

    这好像跟上面没啥不同啊,用来干嘛?别急,OkReflect 还允许你在回调中处理异常。

    String world = OkReflect.on("java.lang.String")
                    .create("Hello World")
                    .call("substring", 6)
                    .error(new OkReflect.OkReflectErrorCallback() {
                        @Override
                        public void onError(@NotNull Exception e) {
                            // 处理异常
                        }
                    })
                    .get();
    

    如果你用的是 Kotin 语言,那你还可以这样写。

    val world: String? = OkReflect.on("java.lang.String")
            .create("Hello World")
            .call("substring", 6)
            .error{
                // 处理异常
            }
            .get()
    

    2. OkReflect 与 jOOR 的其他区别

    1. 连续调用不同方法

    在 jOOR 中,如果你调用的方法是有返回值的,那你下一个调用的方法就会用上一个方法返回的值来调用,当我们并不关心返回值的时候,这种默认操作会带来一些不便之处。假设我们现在有一个 Manager 类。

    public class Manager {
    
        public List<String> items = new ArrayList<>();
        public int count;
    
        public int addData(String data) {
            this.items.add(data);
            count ++;
            return count;
        }
    
        public String getData(int i) {
            return items.get(i);
        }
    
    }
    
    

    假如我们用 jOOR 在添加数据后获取数据,在调用方法后 jOOR 会抛出 NoSuchMethodException 异常,这是因为 jOOR 会使用 addData 返回的 count 来调用 getData 方法,而 count 是 Integer,Integer 中的确没有 getData 方法。

    // jOOR
    String data = on("Manager")
                    .create()
                    .call("addData", "data")
                    .call("getData", 0)
                    .get();
    

    如果使用 OkReflect 进行这个操作,则不会出现这个问题。

    // OkReflect
    String data = OkReflect.on("Manager")
                    .create()
                    .call("addData", "data")
                    .call("getData", 0)
                    .get();
    

    那如果你想在添加数据后忽略返回值,而是要拿到该实例时要怎么做?在 jOOR 中你只能获取返回值的结果,但是在 OkRefelct 中,你可以使用 getInstance 方法来获取实例而不是返回结果,比如下面这这样的

    // OkReflect
    Manager manager = OkReflect.on("Manager")
                                        .create()
                                        .call("addData", "data")
                          .getInstance();
    

    2. 类型安全

    在 jOOR 中,获取的返回值的类型是没有保证的,也就是在 try…catch 中使用 ReflectException 的话包含的范围还太小,要使用 Exception 才能捕获到类型转换异常。

    但是在 OkReflect 中,在返回时对类型转换异常进行了捕获,如果类型不对,则会返回空,这时候你可以使用空值来进行判断是否转换成功,如果你想要的话,你也可以在 ErrorCallback 中处理该异常,比如下面这样的。

    String manager = OkReflect.on("Manager")
                    .create()
                    .call("addData", "data")
                    .error(new OkReflect.OkReflectErrorCallback() {
                        @Override
                        public void onError(@NotNull Exception e) {
                            // 处理异常
                        }
                    })
                    .getInstance();
    
    其他

    OkReflect 的 GitHub 地址:https://github.com/zeshaoaaa/OkReflect

    相关文章

      网友评论

        本文标题:OkReflect:Java 反射框架

        本文链接:https://www.haomeiwen.com/subject/cmxacctx.html