Java 8泛型:使用原始类型引发的类型转换异常问题问询
我来帮你理清这里的核心问题——原始类型和无界通配符看起来相似,但本质是完全不同的东西,这也是你代码里隐藏风险的根源。
先说说原始类型List的问题
当你使用原始类型List(比如代码里的list1)时,Java会彻底关闭泛型类型检查,相当于退回到Java 5之前的非泛型时代。这意味着你可以往这个列表里插入任何类型的对象——就像你代码里做的那样,插入一个String,哪怕这个列表实际被声明为List<Integer>或者List<Double>。
这种操作的隐患不会立刻爆发,而是会在后续代码尝试从列表中取出元素并进行类型转换时,抛出ClassCastException。更麻烦的是,这个错误可能出现在和当前方法完全无关的代码位置,排查起来非常头疼。比如:
List<Integer> intList = new ArrayList<>(); new Wildcards().contains(intList, new ArrayList<String>()); int num = intList.get(0); // 这里会直接抛出ClassCastException!
为什么无界通配符List<?>更安全?
而List<?>(比如代码里的list2)是一个受编译器约束的泛型类型,它表示“一个元素类型未知的List”。Java编译器会严格限制对它的操作:你可以读取里面的元素(但只能当作Object处理),但不允许你往里面添加任何非null的元素——因为编译器不知道这个列表实际应该容纳什么类型的元素,随便插入会破坏类型一致性。
如果把你的代码里的list1改成List<?>,尝试执行list1.add("12sdf34")时,编译器会直接报错,从根源上阻止了这种不安全的操作。
针对你的代码的优化建议
如果你的contains方法只需要遍历list1的元素进行检查,不需要往里面添加元素,那完全可以把list1的参数类型改成List<?>,这样既保持了方法的通用性(可以接受任何类型的List),又彻底避免了类型安全漏洞。修改后的方法签名会是:
public boolean contains(List<?> list1, List<?> list2)
内容的提问来源于stack exchange,提问作者Kirill

