== 在泛型类型中的行为

Behaviour of == with Generic types

提问人:theutonium.18 提问时间:10/19/2023 更新时间:10/19/2023 访问量:104

问:

请考虑以下代码片段:

List<Object> objs1 = Arrays.asList("one", "two");
List<String> strs1 = (List<String>)(List<?>)objs1;

assert strs1 == objs1; //compile error

既然只比较对象身份 - 为什么当它只是强制转换的问题时,它会产生编译错误。==

在考虑以下片段时 - 这里的断言不会失败:

String str = "this is it";
Object oo = str;
assert str == oo; //asserts true

再说一遍 - 我们正在做同样的事情,但它工作正常。

  • 为什么即使所持有的对象相同,运算符也会根据持有对象的引用类型行事?==
Java 泛型转换 JLS

评论

3赞 markspace 10/19/2023
我认为在这里:如果无法通过强制转换(§5.5)将任一操作数的类型转换为另一个操作数的类型,则这是一个编译时错误。两个操作数的运行时值必然不相等(忽略两个值均为 null 的情况)。 docs.oracle.com/javase/specs/jls/se21/html/......
0赞 markspace 10/19/2023
换句话说,你必须使用一个时髦的强制转换(你有两个强制转换)来让编译器接受任务。我认为编译器无法弄清楚如何在一次强制转换中做到这一点,所以你会得到一个错误。我测试了它,如果您使用 而不是 :ifassertif( strs1 == objs1 ) ; //compile error
0赞 knittl 10/19/2023
str == oo可能有效,因为 String IS_A Object(String extends Object)。但 a 不是 a ( 不是 的子类型List<String>List<Object>List<String>List<Object>)
1赞 Old Dog Programmer 10/19/2023
为了将来参考,此类型的问题应包括编译器错误消息的副本。为确保准确性,请使用文本复制和粘贴。

答:

0赞 pdem 10/19/2023 #1

由于类型擦除,您可以在运行时设置和比较不兼容的引用。 泛型出现在 Java 5(此时命名为 1.5)中,在后台,JVM 仍然设法在运行时使用。 编译器说的是,即使你操作相同的引用,也无法与之相提并论。
事实上,从语义上讲,a 不能包含 a,所以你陷入了一个不合逻辑的境地
List<Object>List<Object>List<String>List<Object>List<String>

事实上,“边缘情况”是你用双重强制转换
欺骗了编译器,而不是不应该分配的。在第二行,编译器允许您使用不应该可能的强制转换:
List<String> strs = (List<String>)(List<?>)objs;List<Object>List<String>

    List<Object> objs = Arrays.asList("one", "two"); 
    List<String> strs = (List<String>)(List<?>)objs;// incompatible type affectation with no errors at runtime because of type erasure
    // this compares 2 differents type that the compiler will refuse System.out.println(strs == objs);

如果你想要一个两者的超类型,它必须是,那么代码对于编译器和运行时都是正确的。List<String>List<Object>List<? extends Object>

这有效且语义正确

    List<? extends Object> objs = Arrays.asList("one", "two");
    List<String> strs = (List<String>)(List<?>)objs;
    System.out.println(strs == objs);

现在说到类型擦除,它可能会导致类似的问题:

    List<? extends Object> objs = Arrays.asList("one", "two");
    List<Integer> ints = Arrays.asList((Integer.valueOf(1),Integer.valueOf(2));
    List<String> strs = (List<String>)objs; // this shouldn't work on runtime but does because of type erasure
    System.out.println(strs == objs);

在这里,我可以影响 List of String 变量中的 List of Integer,并且由于类型擦除,我没有得到 classCastException。如果类型保护是完美的,我应该不能 1.将 A 转换为 2。在运行时影响此值。List<Integer>List<String>

评论

0赞 pdem 10/19/2023
@user85421 你是对的,类型推断也使用左侧操作数,在你的情况下,对象不编译。我会进行修改List<Object> objs = Arrays.<String>asList("one", "two");
0赞 pdem 10/19/2023
@user85421感谢已经完成,我(错误地)期望 Arrays.asList(T) 在 T 类型作为参数传递时始终返回 List<T>。
0赞 user85421 10/19/2023
请记住,A 也是一个 - 所以可以两者兼而有之 - 通常编译器会采用更具体的类型,但这不是一个选项,因为赋值会失败StringObjectT
0赞 pdem 10/19/2023
@User85421 这就是陷阱 String 是 Object,但 List String 不是 List Object,正如我所说,它的列表是 ?extends 对象是 String 列表的超类 参见泛型中 extends 和 super 的用法,这是一个复杂的注释问题:stackoverflow.com/questions/4343202/...
0赞 pdem 10/20/2023
是的,我明白这一点,我更新了我的答案,明确表示这种演员阵容不应该是不可能的,这就是问题的根源。编译器拒绝比较不兼容的类型,这不是问题。List<String> strs = (List<String>)(List<?>)objs