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

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了08 字符串连接符 “+“ 导致的 check cast 的省略相关的知识,希望对你有一定的参考价值。

前言

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

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

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

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

以下内容基于 jdk8

测试用例

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

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

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

/**
 * Test03GenericTypeCast
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @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 是表达式泛型擦除之后的类型, 这里两个类型是兼容的, 因此 

因此没有生成这个 check cast 

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

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

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

这个问题是 本问题的关键 

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

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

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

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

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

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

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

jls 中的 String Concatenation Operator +

 

参考

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

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

以上是关于08 字符串连接符 “+“ 导致的 check cast 的省略的主要内容,如果未能解决你的问题,请参考以下文章

POST数据中有特殊符号导致数据丢失的解决方法

sql语句中字符串的连接符是啥?

Flag-PHP-代码审计过check

C宏定义中的连接符"##"和字符串化操作符"# "及变参宏"..."

C语言的宏定义,字符串连接

字符串