泛型擦除后,取出与泛型类型不同的数据疑惑?(彻底研究明白了)

class Message<T>{//类上使用泛型

    private T msg;

    public void setMsg(T msg){

        this.msg=msg;
    }

    public T getMsg(){

        return msg;
    }

}


public class Main {
    public static void main(String[] args) {


        Message m1 = new Message();//泛型擦除
        m1.setMsg(1200);//Object msg=1200;

        Message<String> temp=m1;//把Message<String>换成Message<Boolean>或者Message<Stringbuffer>等都不会报错
                                //唯独写成Message<String>运行时就会出现类转换异常
        System.out.println(temp.getMsg());

    }

}

看了下字节码,发现其他泛型擦除后都是Object
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V


唯独写成Message<String>,泛型擦除后字节码的有一个地方是String类型的。
CHECKCAST java/lang/String
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V

这个CHECKCAST是java虚拟机里面的类型转换检查指令

不过我还是挺疑惑的?写个别的泛型类型都没事,怎么就写个String还蹦出来类型转换检查了???

然后就出现了类转换异常,搞了半天也没看懂????

其实这个疑问涉及到了一些java运行机制的问题

2020.5.17研究(2020.6.6已修改部分错误猜想)
今天仔细的一想,发现只有这句代码出现了异常System.out.println(temp.getMsg());,如果注释掉该行代码,那么程序就不会报错。
检查到设置的泛型类型是String,所以打印输出时会调用PrintStream类的println(String s)方法,这也就是为什么字节码里面会出现PrintStream.println (Ljava/lang/String;)V的原因。

2020.6.6(类转换异常的罪魁祸首)
目前为止到了今天,已经学完了io流,PrintStream类是字节打印流,就是把OutputStream类封装在里面,这样就能避免我们在输出时手动的转成字节才能输出。

当我设置了Message<String> temp时,泛型类型是String。

这里补充一个小知识点。
System.out.println()的System.out是进行屏幕输出的操作对象,是一个PrintStream类的实例化对象。
后面的println()是PrintStream类的方法

所以,当我设置泛型是String时,println方法对应着会调用public void println(String x)

而当我设置泛型是其他类型时,println方法对应着会调用public void println(Object x)(可以看jdk源码分析)

这时候坑就来了,首先,temp.getMsg()得到的是什么?是Object对象(由于泛型擦除Integer向上转型成了Object类型)

所以 System.out.println(temp.getMsg());,这个语句就会把temp.getMsg() 传给这个方法public void println(String x)

就会导致如下操作,直接把一个Object类型的对象赋值给String类型。
String x=temp.getMsg();

由于这一切都是在运行时发生的,编译器没有办法给我们检测出来,所以就会出现类 转 换 异 常


解答:擅用百度,解其疑惑。

http://bbs.itheima.com/thread-13280-1-1.html
这个帖子完美解答了我的疑问,同时在我后期的研究中发现相吻合。

从我在上文列举的字节码不同之处就可以看到有PrintStream.println (Ljava/lang/String;)V,这已类和方法在这个贴子中也同样出现了,所以我点进去一看,恍然大悟。

帖子内容:
定义泛型为Integer时,内部元素取出后的类型应为Integer类型,在打印时就需要调用toString方法。
而定义泛型为String时,内部元素取出后类型应为String类型,在打印String类型时,并不调用其toSting方法(参见PrintStream类print(String s)方法)。而是按照平台的默认字符编码将字符串的字符转换为字节。
有参数可知,当定义泛型为Integer时,传入的参数类型是String类型,但根据泛型限定,它被规定为Integer类型,可以理解为带着Integer类型面具的String对象,打印时调用对象toString方法,而且这个对象本身有toString方法,所以能被打印。
定义泛型为String时,内部元素取出后应为String类型,在打印时不需要调用toString方法,而是直接打印,但其对象实际为Integer类型,编译器不调用toString方法就没有办法打印,此时就发生了实际类型与泛型限定的取出类型不匹配异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值