Java Generics:将原始类型转换为任何可再生类型不会生成未经检查的强制转换警告

yap*_*m01 5 java generics

关于以下代码我有以下问题:

public class GenericBridgeMethods <T> {

    public static void main(String[] args) {
        List obj = new ArrayList<Integer>();
        List <?> l1 = (List<?>) obj; // clause 1
        GenericBridgeMethods <?> g1 = (GenericBridgeMethods<?>) obj; // clause 2
   }

}
Run Code Online (Sandbox Code Playgroud)

一个.第1条当然不会给出未经检查的投射警告
b.第2条也没有给出未经检查的演员警告

我注意到从原始类型(obj)到ANY可再现类型(如GenericBridgeMethods或GenericBridgeMethods <?>)的强制转换不会给出未经检查的强制转换警告.如果运行此代码,则在第2节将发生运行时错误.

编译器不应该在第2条中发出警告

编辑1:

    ArrayList a1 = new ArrayList<Integer>(); // clause 3
    Number n1 = (Number)a1; // clause 4 ERROR
    Comparable c1 = (Comparable)a1; // clause 5

    List l1 = new ArrayList<Integer>(); // clause 6
    Number n2 = (Number)l1; // clause 7
    Comparable c2 = (Comparable)l1; // clause 8
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么只有第4条有错误?

Mar*_*ers 11

好吧,首先GenericBridgeMethods,你已经定义它,T不是一个可再生的类型.可恢复意味着该类型将被编码到类中,并将在运行时可用.事实并非如此T.

第2条没有给出运行时警告,因为它检查:将有一个obj类型可分配给GenericBridgeMethods类型的运行时检查.由于您选择了通配符作为类型参数,因此无需T检查任何内容.

另一方面,如果你做了这样的事情:

GenericBridgeMethods<String> g1 = (GenericBridgeMethods<String>) obj;
Run Code Online (Sandbox Code Playgroud)

让你一个unchecked分配警告,因为事实objGenericBridgeMethodsStrings不能在运行时进行检查.但是,如果您这样做,则会出现相同的警告:

List<String l1 = (List<String>) obj;
Run Code Online (Sandbox Code Playgroud)

编辑

如果您对编译器为什么允许您尝试将a转换List为a 感到困惑GenericBridgeMethods,答案是因为编译器无法知道整个层次结构GenericBridgeMethods及其子类.可能有一个子类GenericBridgeMethods实现List,在这种情况下,强制转换可能是合法的.

但是,如果您创建了一个最终类(因此阻止它具有子类),您收到编译错误GenericBridgeMethods.在这种情况下,您将收到无法解决的类型错误.

只是为了向您展示您的问题与可再生类型和泛型无关,请看一下:

public static void main(String[] args) {
   List obj = new ArrayList<Integer>();

   //this is allowed (no warning), even though it will fail at runtime
   CharSequence sequence = (CharSequence) obj; 
}
Run Code Online (Sandbox Code Playgroud)

即使知道它会在运行时失败,你也可以明确地转换obj为a .原因是因为所有编译器都知道这是一种类型.由于是一个接口,有可能是的实现,这也是一个,所以演员必须被允许.CharSequenceobjListListCharSequenceList

每个显式转换都有一定程度的可能性,它可能在运行时失败.否则,它将是一个冗余转换,编译器应该允许您省略显式转换.

编辑 - 关于你的"编辑#1"

ArrayList a1 = new ArrayList<Integer>(); // clause 3
Number n1 = (Number)a1; // clause 4 ERROR
Comparable c1 = (Comparable)a1; // clause 5

List l1 = new ArrayList<Integer>(); // clause 6
Number n2 = (Number)l1; // clause 7
Comparable c2 = (Comparable)l1; // clause 8
Run Code Online (Sandbox Code Playgroud)

你想知道为什么只有"第4条"不能编译.我想我已在上面和评论中对此进行了解释,但我将逐步为您提供这个具体示例.

ArrayList a1 = new ArrayList<Integer>(); // clause 3
Number n1 = (Number)a1; // clause 4 ERROR
Run Code Online (Sandbox Code Playgroud)

铸造a1Number不工作,因为NumberArrayList都是类,而不是接口.由于Java不允许继承多个,一个对象既实例NumberArrayList,Number将必须的一个子类ArrayList,反之亦然.众所周知,这在编译时是不正确的.

ArrayList a1 = new ArrayList<Integer>(); // clause 3
Comparable c1 = (Comparable)a1; // clause 5
Run Code Online (Sandbox Code Playgroud)

由于Comparable是一个接口,一个子类ArrayList可能是一个Comparable.

List l1 = new ArrayList<Integer>(); // clause 6
Number n2 = (Number)l1; // clause 7
Run Code Online (Sandbox Code Playgroud)

因为List接口是一个Number可以实现的子类List.编译器在检查l1持有的转换时不知道ArrayList.