美文网首页基础知识
Java泛型——利用反射越过泛型检查

Java泛型——利用反射越过泛型检查

作者: NEU_PROYZ | 来源:发表于2018-07-12 20:39 被阅读68次

上一篇提到泛型基本是不可具化的,因为在运行期会将类型擦除
我们也知道泛型擦出的目的主要是为了兼容原生态类型的代码,但是这有没有问题呢!!有,那就是我们可以利用反射越过泛型检查

我们都知道,反射其实就是利用类加载过后的Class对象
或者说就是用的那个.class文件里面的信息,而这里面的字节码是编译过的。
这就意味着我们可以不用经过编译阶段了,那就不会有类型检查
通过反射获取对象后可以获得相应的add方法,并向方法里面传入任何对象。

public class ReflectDemo {
    public static void main(String[] args) throws NoSuchMethodException {
        List<Integer> list = new ArrayList<>(5);
        list.add(1);
        Class<?> targetClass = list.getClass();
        Method addMetohd = targetClass.getDeclaredMethod("add",Object.class);
        Student s = new Student("xiaoming",10);
        try {
            addMetohd.invoke(list, "badType");
            addMetohd.invoke(list,s);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(list.get(0));dui
        System.out.println(list.get(1));
        System.out.println(list.get(2));
    }
}

还有一个Student类,就name和age属性,很简单,大家可以自己去写一个就好了。解释一下上述代码:
①新建一个List<Integer>对象,并向其中插入一个整型,此刻是有泛型检查的。

②我们通过反射获取class对象targetClass,之后获取class的add方法,并传入add方法参数的类型。之所以要传入Object.class,是因为在List<E>中,add方法的参数都是E类型的,所以当类型擦除之后就是Object。

③我们分别往add方法里面传入一个String对象和一个Student对象。

然后打印出信息:


Result.png

此时,这List对象里面就有三种类型的数据。

这个地方有一个坑,大家要注意下
例如:你重写一下toString()方法,并把最后一行代码改成list.get(2).toString()。
你会发现出错了,刚开始我还奇怪了,意思是我能传入String,却不能传入Student对象吗?

其实不是的,问题其实出在toString()方法上。
你点进toString()你会发现它调用的其实是Integer的toString()方法而不是Student的toString()方法。这就有意思了!你其实可以自己想到,这是正常编译的过程,所以编译器当然认为list.get(2)出来的是一个Integer对象啦。

另外,当你利用反射往List<String>里面插入Integer的时候,在print()的时候也会出问题,其实原因都大同小异。此时的print()会调用String的方式,但是参数却是个Integer

有没有想问,为啥反过来就不会出错了呢?因为print()方法没有Integer这种重载的形式,那么它会自动调用Object的形式,当然就不会出错啦。


print.png

ok,既然都讲到这了,我们反编译一下class对象,看一下里面究竟还有没有类型信息。


反编译.png

有没有一脸懵逼,不管你有没有,我反正是有的。咋个反编译回来还有泛型信息啊。放心,运行时类型擦出是没有问题的。这其实是因为idea的编译器给我们生成的,方便查看。
那我们再用java自带的工具反编译来看看:

反编译.png
嗯嗯,可以看到,类型信息其实是在注释里面的是有的。
很早之前看过一篇博文,里面分析的很好class文件的属性表中添加了Signature和LocalVariableTypeTable等属性来支持泛型识别的问题。感兴趣的深追!

相关文章

  • Java泛型——利用反射越过泛型检查

    上一篇提到泛型基本是不可具化的,因为在运行期会将类型擦除。我们也知道泛型擦出的目的主要是为了兼容原生态类型的代码,...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • spring 泛型处理

    java 泛型基础 泛型类型:泛型类型是在类型上参数化的泛型类或接口 泛型使用场景编译时前类型检查。定义为 Col...

  • 第二十八课:泛型

    泛型出现之前 泛型出现之后 Java深度历险(五)——Java泛型

  • Java反射绕过泛型检查

    在java的泛型中,实际上只是编译器认识泛型,而虚拟机是不认识泛型的。简单来讲,在运行时,所有的泛型都是objec...

  • 泛型:类型擦除

    Java 语言引入泛型是为了在编译时提供更严格的类型检查,并支持泛型编程。 为了实现泛型,Java编译器将类型擦除...

  • Think in Java 回顾之泛型

    什么是泛型? Java SE 5 开始引入了泛型的概念,泛型即参数化类型,利用泛型我们可以编写出更通用的代码(先不...

  • Java泛型

    参考:Java知识点总结(Java泛型) 自定义泛型类 自定义泛型接口 非泛型类中定义泛型方法 继承泛型类 通配符...

  • Kotlin 泛型

    说起 kotlin 的泛型,就离不开 java 的泛型,首先来看下 java 的泛型,当然比较熟悉 java 泛型...

网友评论

    本文标题:Java泛型——利用反射越过泛型检查

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