反射工具类,如斯优雅[通俗易懂]

反射工具类,如斯优雅[通俗易懂]反射的作用我在这就不多说了,每次用到反射都是那么一坨代码丢进去,总是让人觉得很不优雅,如今有了我这个反射工具类,那么大家就可以一句话优雅地来完成反射的工作,该工具类是站在 jOOR 的肩膀上进行改造,修复了它没有完成的工作,至于修复了什么,后面源码分析会详述,至于这个工具类在哪…

大家好,欢迎来到IT知识分享网。

logo

Foreword

反射的作用我在这就不多说了,每次用到反射都是那么一坨代码丢进去,总是让人觉得很不优雅,如今有了我这个反射工具类,那么大家就可以一句话优雅地来完成反射的工作,该工具类是站在 jOOR 的肩膀上进行改造,修复了它没有完成的工作,至于修复了什么,后面源码分析会详述,至于这个工具类在哪,现已加入至 1.12.0 版本的 AndroidUtilCode,下面来介绍下其功能。

Functions

其 APIs 如下所示:

反射相关 -> ReflectUtils.java -> Test

reflect : 设置要反射的类
newInstance: 实例化反射对象
field : 设置反射的字段
method : 设置反射的方法
get : 获取反射想要获取的

IT知识分享网

Use

实例化反射对象

比如,我们实例化一个 String 对象可以这样做:

IT知识分享网String str1 = ReflectUtils.reflect(String.class).newInstance().get();
// equals: String str1 = new String();

String str2 = ReflectUtils.reflect("java.lang.String").newInstance("abc").get();
// equals: String str2 = new String("abc");

String str3 = ReflectUtils.reflect(String.class).newInstance("abc".getBytes()).get();
// equals: String str3 = new String("abc".getBytes());

设置反射的方法

比如,我们想要调用 Stringsubstring 函数可以这样做:

String str1 = ReflectUtils.reflect((Object) "1234").method("substring", 2).get();
// equals: String str1 = "1234".substring(2);

String str2 = ReflectUtils.reflect((Object) "1234").method("substring", 0, 2).get();
// equals: String str1 = "1234".substring(0, 2);

设置反射的字段

比如,TestPrivateStaticFinal.java 如下所示:

IT知识分享网public class TestPrivateStaticFinal {
    private static final int     I1 = new Integer(1);
    private static final Integer I2 = new Integer(1);
}

我们要设置其 I1I2 值为 2,可以如下操作:

ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I1", 2);
ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I2", 2);

要获取其 I1I2 值的话,可以如下操作:

ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I1").get()
ReflectUtils.reflect(TestPrivateStaticFinal.class).field("I2").get()

当然,字段操作也有更高级的操作,比如 Test1.java 测试类如下所示:

public class Test1 {
    public static int     S_INT1;
    public static Integer S_INT2;
    public        int     I_INT1;
    public        Integer I_INT2;

    public static Test1 S_DATA;
    public        Test1 I_DATA;
}

我对其进行的单元测试如下所示:

@Test
public void fieldAdvanced() throws Exception {
    ReflectUtils.reflect(Test1.class)
            .field("S_DATA", ReflectUtils.reflect(Test1.class).newInstance())// 设置 Test1.class 中 S_DATA 字段 为 new Test1()
            .field("S_DATA")// 获取到 Test1.class 中 S_DATA 字段
            .field("I_DATA", ReflectUtils.reflect(Test1.class).newInstance())// 获取到 Test1.class 中 S_DATA 字段 的 I_DATA 为 new Test1()
            .field("I_DATA")// 获取到 Test1.class 中 S_DATA 字段 的 I_DATA 字段
            .field("I_INT1", 1)// 设置 Test1.class 中 S_DATA 字段 的 I_DATA 字段的 I_INT1 值为 1
            .field("S_INT1", 2);// 设置 Test1.class 中 S_DATA 字段 的 S_INT1 字段的 I_INT1 值为 2
    assertEquals(2, Test1.S_INT1);// 静态变量就是最后设置的 2
    assertEquals(null, Test1.S_INT2);// 没操作过就是 null
    assertEquals(0, Test1.S_DATA.I_INT1);// 没操作过就是 0
    assertEquals(null, Test1.S_DATA.I_INT2);// 没操作过就是 0
    assertEquals(1, Test1.S_DATA.I_DATA.I_INT1);// 倒数第二步操作设置为 1
    assertEquals(null, Test1.S_DATA.I_DATA.I_INT2);// 没操作过就是 null
}

根据如上注释相信大家也可以理解一二了,如果还想了解更多使用方式,可以查看我写的单元测试类 ReflectUtilsTest,其使用方式就介绍到这里,下面介绍其实现方式。

Achieve

实现的话是站在 jOOR 的肩膀上进行改造,其内部封装了一个 private final Object object; 变量,每次进行反射操作时都会重新实例化一个变量并把结果赋予该变量,最终 get() 就是获取其值,比如我们来看一下 newInstance 的操作,其涉及的代码如下所示:

/**
 * 实例化反射对象
 *
 * @param args 实例化需要的参数
 * @return {@link ReflectUtils}
 */
public ReflectUtils newInstance(Object... args) {
    Class<?>[] types = getArgsType(args);
    try {
        Constructor<?> constructor = type().getDeclaredConstructor(types);
        return newInstance(constructor, args);
    } catch (NoSuchMethodException e) {
        List<Constructor<?>> list = new ArrayList<>();
        for (Constructor<?> constructor : type().getDeclaredConstructors()) {
            if (match(constructor.getParameterTypes(), types)) {
                list.add(constructor);
            }
        }
        if (list.isEmpty()) {
            throw new ReflectException(e);
        } else {
            sortConstructors(list);
            return newInstance(list.get(0), args);
        }
    }
}

private Class<?>[] getArgsType(final Object... args) {
    if (args == null) return new Class[0];
    Class<?>[] result = new Class[args.length];
    for (int i = 0; i < args.length; i++) {
        Object value = args[i];
        result[i] = value == null ? NULL.class : value.getClass();
    }
    return result;
}

private void sortConstructors(List<Constructor<?>> list) {
    Collections.sort(list, new Comparator<Constructor<?>>() {
        @Override
        public int compare(Constructor<?> o1, Constructor<?> o2) {
            Class<?>[] types1 = o1.getParameterTypes();
            Class<?>[] types2 = o2.getParameterTypes();
            int len = types1.length;
            for (int i = 0; i < len; i++) {
                if (!types1[i].equals(types2[i])) {
                    if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) {
                        return 1;
                    } else {
                        return -1;
                    }
                }
            }
            return 0;
        }
    });
}

private ReflectUtils newInstance(final Constructor<?> constructor, final Object... args) {
    try {
        return new ReflectUtils(
                constructor.getDeclaringClass(),
                accessible(constructor).newInstance(args)
        );
    } catch (Exception e) {
        throw new ReflectException(e);
    }
}

private final Class<?> type;

private final Object object;

private ReflectUtils(final Class<?> type, Object object) {
    this.type = type;
    this.object = object;
}

jOOR 所没有做到的就是没有对多个符合的 Constructor 进行排序,而是直接返回了第一个与之匹配的。这样说有点抽象,我举个例子应该就明白了,比如说有两个构造函数如下所示:

public class Test {

    public Test(Number n) {
    }

    public Test(Object n) {
    }
}

jOOR 反射调用构造函数参数传入 Long 类型,很可能就会走 Test(Object n) 这个构造函数,而我修改过后就是对多个符合的 Constructor 进行排序,匹配出与之最接近的父类,也就是会走 Test(Number n) 这个构造函数,同理,在后面的 method 中的参数匹配 jOOR 也是存在这个问题,我也已经对其修复了。

还有就是 jOOR 对 private static final 字段先 getset 会报异常 java.lang.IllegalAccessException 异常,是因为对 private static final 字段 get 的时候没有去除 final 属性,如果在 get 时就把 final 去掉即可解决,那样在 set 的时候就不会报错。然而,在 Android 的 SDK 中是没有 Field.class.getDeclaredField("modifiers") 这个字段的,所以会报 NoSuchFieldException 异常,这方面我做了容错处理,相关代码如下所示:

/** * 设置反射的字段 * * @param name 字段名 * @return {@link ReflectUtils} */
public ReflectUtils field(final String name) {
    try {
        Field field = getField(name);
        return new ReflectUtils(field.getType(), field.get(object));
    } catch (IllegalAccessException e) {
        throw new ReflectException(e);
    }
}

/** * 设置反射的字段 * * @param name 字段名 * @param value 字段值 * @return {@link ReflectUtils} */
public ReflectUtils field(String name, Object value) {
    try {
        Field field = getField(name);
        field.set(object, unwrap(value));
        return this;
    } catch (Exception e) {
        throw new ReflectException(e);
    }
}

private Field getField(String name) throws IllegalAccessException {
    Field field = getAccessibleField(name);
    if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
        try {
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        } catch (NoSuchFieldException ignore) {
            // runs in android will happen
        }
    }
    return field;
}

private Field getAccessibleField(String name) {
    Class<?> type = type();
    try {
        return accessible(type.getField(name));
    } catch (NoSuchFieldException e) {
        do {
            try {
                return accessible(type.getDeclaredField(name));
            } catch (NoSuchFieldException ignore) {
            }
            type = type.getSuperclass();
        } while (type != null);
        throw new ReflectException(e);
    }
}

private Object unwrap(Object object) {
    if (object instanceof ReflectUtils) {
        return ((ReflectUtils) object).get();
    }
    return object;
}

所以该工具类既完美支持 Java,也完美支持 Android。

Conclusion

好了,这次反射工具类就介绍到这了,是不是觉得如斯优雅,如果觉得好的话以后遇到反射的问题,那就快用我这个工具类吧,这么好的东西藏着不用真的是可惜了哦。

关于安卓核心常用工具类我已经差不多都封装了,今后应该也不会在核心的里面新增了,除非确实很需要我才会再新增某个工具类,其余不常用的我都会放在 subutil 中,感谢大家一直陪伴着 AndroidUtilCode 的成长。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/13352.html

(0)
上一篇 2023-03-11 18:11
下一篇 2023-03-11 18:13

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信