2012-10-15 16 views
2
import java.util.List; 
import java.util.ArrayList; 

interface Canine {} 
class Dog implements Canine {} 
public class Collie extends Dog { 
    public static void main(String[] args){ 
     List<Dog> d = new ArrayList<Dog>(); 
     List<Collie> c = new ArrayList<Collie>(); 
     d.add(new Collie()); 
     c.add(new Collie()); 
     do1(d); do1(c); 
     do2(d); do2(c); 
    } 
    static void do1(List<? extends Dog> d2){ 
     d2.add(new Collie()); 
     System.out.print(d2.size()); 
    } 
    static void do2(List<? super Collie> c2){ 
     c2.add(new Collie()); 
     System.out.print(c2.size()); 
    } 
} 

此问题的答案表明,当方法采用通配类型typ时,可以访问或修改集合,但不能同时采用这两种。 (凯西和Bert)泛型较低的未绑定vs较高的有界通配符

是什么意思“当一个方法接受一个通配符通用典型值,收集可以访问或修改,但不能同时”?

据我所知, 方法do1有List<? extends Dog> d2所以d2只能访问但不能修改。 方法d2有List<? super Collie> c2所以c2可以被访问和修改,并且没有编译错误。

Generic guidelines

+0

请参见[Java中的'super'和'extends'有什么区别](http://stackoverflow.com/questions/1910892/what-is-the-difference-between-super-and-extends- in-java-generics) – Jesper

+0

请参阅[Java泛型:什么是PECS?](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) – Jesper

+0

请参见[泛型中的通配符:“?超级T“同时工作”?扩展T“不?](http://stackoverflow.com/questions/7541849/wildcards-in-generics-super-t-works-while-extends-t-does-not) – Jesper

回答

0

此问题的答案表明,当方法采用通配类型typ时,可以访问或修改集合,但不能同时采用这两个集合。 (凯西和伯特)

这是一个公平的第一个近似值,但不完全正确。更正确的是:

您只能将null添加到Collection<? extends Dog>,因为其add方法的参数为? extends Dog。无论何时调用一个方法,都必须传递属于声明参数类型的子类型的参数;但对于参数类型? extends Dog,如果表达式为null,编译器只能确定该参数是兼容类型。但是,您当然可以通过致电clear()remove(Object)修改集合。另一方面,如果您从Collection<? super Dog>读取,则其迭代器的返回类型为? super Dog。也就是说,它将返回属于某个未知超类型Dog的子类型的对象。但不同的是,收集可能是Collection<Object>,仅包含String的实例。因此

for (Dog d : collection) { ... } // does not compile 

,所以我们唯一知道的就是对象的实例返回,即迭代的唯一类型,正确的方法这样的收藏是

for (Object o : collection) { ... } 

但可以从阅读收集,你只是不知道你会得到什么类型的对象。

我们可以很容易地笼统地说观察到:鉴于

class G<T> { ... } 

G<? extends Something> g; 

我们只能通过空与声明的类型T方法的参数,但我们可以调用与返回类型T方法,并将结果赋值为Something类型的变量。

在另一方面,对于

G<? super Something> g; 

我们可以通过Something类型的任何表达方法参数与声明类型T,并且我们可以调用与返回类型T方法,但只分配结果提供给变量类型为Object。总而言之,通配符类型使用的限制仅取决于方法声明的形式,而不取决于方法的作用。

+2

'add(null)'也可以 – irreputable

+0

是的,null,它可以被分配给任何类型的任何引用,包括所有类型延长Dog – Joe

+0

@irreputable:修正,谢谢指出。 – meriton

1

我粘贴代码到我的IDE。下面的错误是信号内do1

Add方法(?捕获#1的扩展犬)在类型列表中是不适用的参数(牧羊犬)

这就是,当然,如预期的那样。

0

我把你的代码粘贴到IDEONE http://ideone.com/msMcQ。它没有为我编译 - 这是我的预期。你确定你没有任何编译错误?

+0

我知道有一个编译器在尝试添加元素时,在do1方法中发生错误。我的疑问是关于Katy&Bert说的。 '当一个方法采用通配类型typ时,可以访问或修改该集合,但不能同时使用' 和<?超级牧羊犬>我可以访问和添加元素。 – Joe

+0

@Joe。好吧,我误解了你的问题,并认为你暗示你的代码编译。现在,你不能访问'do2'方法中的元素。 'c2.size()'不算作访问。访问将会像“Collie c = c2”一样。 get(0);'我修改了我的示例http://ideone.com/msMcQ,代码示例 – emory

1

您根本不能将Collie添加到List<? extends Dog>,因为此引用可能包含例如List<Spaniel>

2

您不能将Cat添加到List<? extends Animal>,因为您不知道是哪种类型的列表。这也可能是List<Dog>。所以你不想把你的Cat变成Black Hole。这就是为什么modificationList声明这种方式是不允许的。

同样,当你从List<? super Animal>中取出某些东西时,你不知道你会从中得到什么。您甚至可以获得ObjectAnimal。但是,您可以在此List中安全地添加Animal