2017-09-06 64 views
2

一个例子是从书上采取布鲁斯Eckel的书 问题在评论编译器不会产生错误为什么?因为类型的不变性违反

class GenericWriting{ 
static <T> void writeExact(List<T> list, T item) { 
    list.add(item); 
} 
static List<Apple> apples = new ArrayList<Apple>(); 
static List<Fruit> fruits = new ArrayList<Fruit>(); 
static void f1() { 
    writeExact(apples, new Apple()); 
    writeExact(fruits, new Apple()); /* why the compiler does not produce an error because the invariance of types is violated */ 
} 
static <T> void writeWithWildcard(List<? super T> list, T item) { 
    list.add(item); 
} 
static void f2() { 
    writeWithWildcard(apples, new Apple()); 
    writeWithWildcard(fruits, new Apple()); 
} 

public static void main(String[] args) { 
    f1(); 
    f2(); 
} 
} 

回答

0

这是由于多态性。

通常情况下,如果你写这样的方法:

private static void method(Fruit f) {} 

您可以用new Apple()称之为:

method(new Apple()); 

吧?因为AppleFruit的一个子类。

当你打电话给你的方法:

writeExact(fruits, new Apple()); 

。因为没有明确指定T,编译器试图推断出它。它看到第一个参数fruitsList<Fruit>。它认为T必须是Fruit。现在,你的方法是这样的:

static void writeExact(List<Fruit> list, Fruit item) 

看到了吗?因为AppleFruit的子类,所以将苹果传递给第二个参数是完全正确的。

在另一方面,如果指定的通用T to be Apple`,就会发生错误:

List<Fruit> list = new ArrayList<>(); 
// compiler error! 
GenericWriting.<Apple>writeExact(list, new Apple()); 

因为List<Fruit>是不兼容List<Apple>

+0

GenericWriting。 writeExact(list,new Apple()); 这意味着现在的方法参数应该完全是Apple? – SergiiGusar

+0

您可以解释空白m(?t) 和 void m(?t) ? – SergiiGusar

+0

@SergiiGusar的?东西被称为“通用通配符”。在google或堆栈溢出搜索这个来找出它的含义。例如:https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html并且关于您的第一个问题,该方法现在变为'(列表,Apple)'。你必须传递一个'List ',比如'ArrayList ','List '不会因为泛型的不变性而起作用。 – Sweeper

相关问题