<t extends =“”aninterface =“”> vs <! - ?扩展AnInterface - > </t>

时间:2012-08-14 15:17:41

标签: java generics

我对某些事感到困惑。

我有一个不是集合的类,但它确实引用了通用对象:

    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);

这样我可以将Supertype的多个子类型传递给泛型类......

我没有看到使用“?”的目的使用Interface作为通用参数有什么问题吗?

4 个答案:

答案 0 :(得分:6)

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

另一方面,如果您将XClass定义为:

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

然后表达式xc.add(b);会产生编译错误,因为添加的所有对象必须与声明的类型相同,在本例中为A.

如果您将xc声明为,例如:

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

然后再添加ab是不合法的,因为我们唯一知道的是xc是某个未知的但固定的子类型{{1} },并且无法知道该未知类型是AnInterface还是A还是其他任何类型。

但是,假设您正在编写一个接受XClass类型的方法,您可以迭代之前添加的元素。您唯一的限制(为了示例)是项目扩展B,您不关心实际类型是什么。

您可以将此方法声明为:

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>

答案 1 :(得分:0)

如果您希望让XClass的实例只使用E的一个子类而不使用其他不扩展/实现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(),您只能在添加方法中使用ClassOne的对象而不是ClassTwo中的对象。用?允许您传入任何实施AnInterface的课程,ClassOneClassTw o。

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

答案 2 :(得分:0)

在您的示例中,您需要在“添加”方法中使用type erasure,因此您不应在班级中使用通配符。

通配符仅在您不需要类型擦除时使用(即,只要它是...的子类,您不关心类型),并且还需要subtype泛型本身。

答案 3 :(得分:0)

通配符只是意味着它将是符合该标准的某个类。那么? extends 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));