前言

// 年轻时候,到了冬天,家人让你穿秋裤,你不仅不穿秋裤,还露着脚脖子,如果有人劝你,你会嫌他唠叨。而等你岁数大一点,天气一冷,身体受不了,就自觉把秋裤穿上了。

呵呵 昨天的时候发现了这样的一个问题 泛型强转类型之后取数据的一个疑问,问题不好描述请看代码和截图吧。, 然后记录了一个 todo, 来看了一下

还是挺有意思的, 作者 本身也做了一定的思考

本以为 这个没有多少可以记录的内容, 没有想到 还是有一些细节的地方

以下内容基于 jdk8

测试用例

测试用例的主要内容来自于文章 泛型强转类型之后取数据的一个疑问,问题不好描述请看代码和截图吧。 | HeapDump性能社区

主要的问题在于 上面的 listTest.get 没有做 check cast, 下面的 listStr 做了 check cast 

按正常的逻辑来考虑, listTest.get 应该是也需要做 check cast 

/**
 * Test03GenericTypeCast
 *
 * @author Jerry.X.He 
 * @version 1.0
 * @date 2022-02-08 09:27
 */
public class Test03GenericTypeCast {

    // Test03GenericTypeCast
    public static void main(String[] args) throws Exception {

        ArrayList<String> listStr = new ArrayList<String>();
        listStr.add("只能添加String类型");

        Class c2 = listStr.getClass();
        Method m = c2.getMethod("add", Object.class);
        m.invoke(listStr, 20);//注意listStr只能添加String类型,我们现在可以通过反射的手段绕过编译器给他加一个整形进去
        System.out.println("注意整形已经添加进去了,List的元素个数为:->       " + listStr.size());

        System.out.println();

        Object obj = listStr;
        ArrayList<Boolean> listTest = (ArrayList<Boolean>) obj;
//        ArrayList<Integer> listTest = (ArrayList<Integer>) obj;
//        ArrayList<StringBuffer> listTest = (ArrayList<StringBuffer>) obj;
        System.out.println("看看listTest的Class类型到底是什么类型:->    " + listTest.getClass());
        System.out.println("我想得到一个Boolean类型,但是得到确实String类型,这是为什么,为什么这里没报错?:->    " + listTest.get(0));
        System.out.println("我想得到一个Boolean类型,但是得到确实String类型,这是为什么,为什么这里没报错?:->    " + listTest.get(1));
        System.out.println("上面没报错就很奇怪,一定要搞明白");

        System.out.println();

        System.out.println("下面的会报错我能理解,上面的没报错我就不能理解?");
        System.out.println("listStr的toString方法,注意整形也可以输出出来:    " + listStr.toString());
        System.out.println("看看listStr的Class类型到底是什么类型:->    " + listStr.getClass());
        //Integer in = list2.get(1);编译直接就报错了
        System.out.println(listStr.get(1));//这里会什么又会报错了,因为lis2认为20是String类型的,结果就报错了,java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String


    }

}

javac 层面的分析

最直接的来看一下 javac 到底做了什么 ?, 这是第一个层次 

先看一下 "listTest.get(0)" 这里面 pt 表示的是表达式期望的类型, mt.returnType 是表达式泛型擦除之后的类型, 这里两个类型是兼容的, 因此 

08 字符串连接符 “+“ 导致的 check cast 的省略_cast

因此没有生成这个 check cast 

08 字符串连接符 “+“ 导致的 check cast 的省略_cast_02

再来一下 "listStr.get(0)", 期望的类型是 String, 然后 泛型类型擦除之后的类型是 Object, 不兼容于 String, 因此 增加了一个 check cast 

08 字符串连接符 “+“ 导致的 check cast 的省略_javac_03

 类型不兼容, javac 增加了一个 check cast 

08 字符串连接符 “+“ 导致的 check cast 的省略_Test_04

listTest.get(0) 和 listStr.get(0) 的类型来自于哪里?

这个问题是 本问题的关键 

listTest.get(0) 的类型来自于这里的 + operator 的第二个参数, 类型是 Object 

08 字符串连接符 “+“ 导致的 check cast 的省略_Test_05

然后这个约束是来自于 字符串连接符 "+"  

具体代码层面的 resolve 是在这里 

08 字符串连接符 “+“ 导致的 check cast 的省略_cast_06

具体的 字符串连接符 "+" 的一些类型约束 

我们这里表达式 "我想得到一个Boolean类型,但是得到确实String类型,这是为什么,为什么这里没报错?:->    " + listTest.get(0) 

左边的操作数是一个 string, 右边的操作数匹配不上 原始类型 或者 String, 因此匹配的是 String + Object 

08 字符串连接符 “+“ 导致的 check cast 的省略_System_07

listStr.get(0) 的类型, 取决于 System.out.println, 选择的是 类型 String 

jls 中的 String Concatenation Operator +

08 字符串连接符 “+“ 导致的 check cast 的省略_javac_08

 

08 字符串连接符 “+“ 导致的 check cast 的省略_System_09

参考

泛型强转类型之后取数据的一个疑问,问题不好描述请看代码和截图吧。 | HeapDump性能社区

03 为Map.put的增加的checkcast & 增加了一段业务无关的 instance.getClass() 的调用_970655147的专栏