2012-08-14 100 views
4

我对某事有些困惑。<T扩展AnInterface> vs <?扩展AnInterface>

我有一个类,其中它不是一个集合,但它确实是指通用对象:如果您

public class XClass<E extends AnInterface>{ 

     E instanceobject; 

     public void add(E toAdd){} 
    } 

    public interface AnInterface{} 

    public class A implements AnInterface{} 

    public class B implements AnInterface{} 

我相信我读的地方,<? extends AnInterface>被使用(声明XClass的实例时)同时需要泛型对象中的多个子类型,而<T extends AnInterface>只允许您在泛型类中同时拥有单一类型的子类型?

不过,我可以只使用:

XClass<AnInterface> xc = new XClass<AnInterface>(); 

    A a = new A(); 
    B b = new B(); 

    xc.add(a); 
    xc.add(b); 

而这样一来,我可以在超型的多种亚型的泛型类通过......

我没有看到的宗旨使用“?”并且使用Interface作为泛型参数有什么问题吗?

回答

4

您可以添加类型为AB的对象的原因是由于您使用接口参数化了您的XClass,因此添加两个实现该接口的不同类是没有任何问题的。

如果,另一方面,你已经定义XClass为:

XClass<A> xc = new XClass<A>(); 

则表达式xc.add(b);会给出一个编译错误,因为所有的对象加入必须有相同的类型被宣布,在这情况下,A

如果声明你xc作为,例如:

XClass<? extends AnInterface> xc = new XClass<AnInterface>();

那么它不是法律不再添加ab,因为我们唯一知道的是,XC是一些未知但固定亚型AnInterface的,并没有办法知道那个未知类型为AB或任何其他。

但假设您正在编写一个接受XClass类型的方法,您可以遍历之前添加的元素。你唯一的限制(就这个例子而言)是项目延伸AnInterface,你不关心实际的类型是什么。

你可以声明此方法,如:

public static void dummyMethod(XClass<? extends AnInterface> dummy){ 
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild. 
} 

现在你可以传递到像XClass<A>XClass<B>XClass<AnInterface>这种方法什么,它都将是有效的。

请记住,您无法添加到您传递的对象,出于上述相同的原因。我们不知道未知类型是什么!

public static void dummyMethod(XClass<? extends AnInterface> dummy){ 
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild. 
    dummy.add(new A()); //you can't do this, we have no idea what type ? stand for in this case 
} 
+0

阿酷 - 我完全明白这一点!那么使用“?”有什么意义?通配符 – mezamorphic 2012-08-14 15:32:33

+0

'?扩展Foo“意味着有一些_fixed_,但是_unknown_,扩展了'Foo'的类型,即”实际“泛型参数。 – 2012-08-14 15:44:22

+0

增加了一个使用'?'通配符的例子。希望现在更加明确。 – pcalcao 2012-08-14 16:04:52

0

您可以使用E如果你想有XClass的实例只使用一个的AnInterface子类,并实现AnInterface不扩展没有其他的类/实现E

例如给出 public class ClassOne implements AnInterface {}public class ClassTwo implements AnInterface {}

如果你使用 public class XClass<E extends AnInterface><ClassOne>XClass xc = new <ClassOne>XClass()那么你只能在你的add方法不ClassTwo的一个使用的ClassOne的对象。使用?将允许您通过任何实施AnInterfaceClassOneClassTw o的课程。

使用标识符E的意思是“对于这个对象我想使用类型E和任何子类”,使用?意思是“我想使用与表达式匹配的任何类型”

0

在你的例子中,你需要在方法“add”中使用type erasure,所以你不应该在你的类中使用通配符。

通配符只用于不需要类型擦除(即,只要它是..的子类就不关心该类型)以及您需要对泛型本身进行subtype

0

通配符只是表示它将会是某些符合条件的类。那么?扩展AnInterface意味着它将是一个(并且只有一个)扩展AnInterface的类。

所以它可能是:

XClass<Impl1> 
XClass<Impl2> 

等等

然而,在运行时,你不知道这个类将是什么。由于这个原因,调用实际类型作为参数的方法本质上是不安全的,因为编译器不可能知道该参数是否适合于实际的实例化实例。

以列表为例。事情可能会声明如下:

List<? extends Number> list = new ArrayList<Integer>(); 

,如果你尝试做任何的这些会发生什么:

list.add(new Double(0)); 
list.add((Number) new Long(1L)); 

它不会编译,因为泛型参数类型是在编译时未知。因此,编译器无法确定Double或Number是否适合传递给实际实例(本例中为ArrayList<Integer>)。这是当你得到臭名昭着的捕获编译错误。

但是这是允许的,因为您在编译时确定该列表可以接受任何Number(其中包括子类)的实例。

List<Number> list = new ArrayList<Number>(); 
list.add(new Double(0)); 
list.add((Number) new Long(1L));