为什么这里会抛出java.lang.ClassCastException

le0nnnnnnn 发布于 09/09 10:25
阅读 213
收藏 3

测试泛型擦除时,使用java的类时,会在运行时抛出java.lang.ClassCastException: java.base/java.lang.Integer cannot be cast to java.base/java.lang.String,但是使用我自己创建的类时(User和Roles是我自己创建的),运行是可以通过的,这是为什么,难道是我们自己创建的类和java预设的类不一样吗

    @Test
    public void fun02() {
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        List list1 = list;
        List<String> ls = list1;
        System.out.println(ls.get(0));
    }
 java.lang.ClassCastException


    @Test
    public void fun03() {
        List<User> list = new ArrayList<User>();
        list.add(new User());
        list.add(new User());
        List list1 = list;
        List<Role> ls = list1;
        System.out.println(ls.get(0));
    }

    public class User {
    private Integer id; 
    private String username;
    private String sex;
    private Date birthday;
    private String address;

    public class Role implements Serializable{
    private Integer roleId;
    private String roleName;
    private String roleDesc;
    private List<User> users = new ArrayList<User>();

   

 


    
 

加载中
1
tcxu
tcxu

唯独 String 的 "擦边球", JVM "不能容忍"!

---"擦边球/钻空子/" 和 JVM "可以容忍"的错误

以 fun03() 方法中的两行代码为例:
    

    List list1 = list;
    List<Role> ls = list1;


头一行代码 将  List<User> 类型的引用 list,赋予一个未注明是何种泛型的 List 类型 的引用
list1 (即 楼主所指的"泛型擦除时"?),这是 "钻空子,致使编译器不报错"。下一行,则将这个 未注明是何种泛型的 List 类型、实为 List<User> 型的引用 list1,赋予 List<Role>的引用,同样是 "钻了编译器的空子"。这就是"擦边球"。因为,若将上述两行代码写成一行, 即直接将 List<User> 型的引用,赋予 List<Role> 型的引用:

     List<Role> ls = list;


那么就通不过编译了。这里的"擦边球",以一个未注明是何种泛型的 List 类型的引用为 "过渡", 便能"成功闯过编译关"。尽管如此,最终 System.out.println(ls.get(0)) 还是如实打印出信息:"User@6d06d69c", 意思是 "在内存 6d06d69c 的 User 类"。如此看来,这个"擦边球"错误,是 "可以容忍"的,就是说,JVM 不会因此而抛出异常而停机。

String 类的常量对象的存储,与其他自定义/类库里定义的类不同,是放到专门的常量池中。因此,任何一个类的对象,无论是自定义的类,还是类库里定义的类,都不能"直接(强制)"转换成 String 类的常量对象, 只能调用方法以生成可以提供信息的字符串。 如:
Integer i=new Integer("12");
String s = (String)i; //"直接(强制)"转换不行。
String s = i.toString(); //调用方法进而生成可以提供信息的字符串,可行。
所以,若将"擦边球"打给 String 类的引用,就是意味着:要"直接(强制)"转换成 String, 那就不会成功了。反之,只要"擦边球"不打给 String类的引用,那就都会成功。

因此,以 fun02() 方法中的两行代码为例:
     

      List list1 = list;
      List<String> ls = list1;


若将 List<String> ls = list1; 中的泛型参数 String 换成任何一个非 String 类,如,StringBuilder/StringBuffer/Double/...,
打"擦边球"的做法,都能成功。

tcxu
tcxu
回复 @le0nnnnnnn : 只要把 ls 当做 List<User> 类型的引用使用,就不会出错,因为 ls 的引用值仍然指向 List<User> 类型的实体(list)。
tcxu
tcxu
回复 @le0nnnnnnn : 是的,只要不涉及到类相关的操作, JVM 都不会报错。记住:由于引用占用的内存都是4个字节,存在 list 的 List<User>型的引用值,最终赋予 ls。这个接力传值过程,引用值是不变的(仍是 list 的值)。就是说,ls 的引用值,仍然指向 List<User> 类型的实体(list)。
le0nnnnnnn
le0nnnnnnn
大佬,我还想再请教一下,如果我在fun03后面再加上这样的语句; ls.get(0).getRoleId(); 这个时候会报类转化异常,我想问一下,如果在运行期间泛型是被擦除的,那么这里报错是因为调用方法的时候报错吗?那么是不是可以理解,只要不涉及到类相关的操作,jvm都不会报错呢
le0nnnnnnn
le0nnnnnnn
谢谢大佬!!!困扰了我两三天了..大佬还是厉害!
0
zzuqiang
zzuqiang
不能这样子玩的,兄台。你想要的范型擦出不是这样子理解的。
le0nnnnnnn
le0nnnnnnn
我也用反射做过泛型擦除,我现在就是不太懂,为什么这两个测试,一个会报错,一个不会报错.
0
染墨若流云
染墨若流云
类型都不一致,晕死,范型不是这么用吧,我不专门做java都看出来了
染墨若流云
染墨若流云
好吧,我错了,你慢慢研究
le0nnnnnnn
le0nnnnnnn
大哥,你再看看题目好吗,我问的是,系统的两个类运行不通过,但是我自己创建的类是可以通过的.而且,我是为了试试泛型和补偿的时机,
返回顶部
顶部