8

我有点困惑。

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

    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 的多个子类型传递给泛型类......

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

4

4 回答 4

8

之所以可以添加这两种类型的对象,AB因为您使用接口对 XClass 进行了参数化,因此添加两个实现该接口的不同类没有任何问题。

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

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

那么表达式xc.add(b);将给出编译错误,因为添加的所有对象都必须具有与声明的类型相同的类型,在本例中为 A。

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

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

a那么添加or就不再合法了b,因为我们唯一知道的是 xc 是 的某个未知但固定的子类型AnInterface,并且无法知道该未知类型是AorB还是其他任何类型。

但是,假设您正在编写一个方法来接受一个 XClass 类型,您可以迭代之前添加的元素。您唯一的限制(为了示例)是项目 extend 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
}
于 2012-08-14T15:26:53.063 回答
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()那么你只能ClassOne在你的 add 方法中使用一个对象,而不是ClassTwo. 使用 ?将允许您传入任何实现的类AnInterface,要么ClassOneClassTwo。

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

于 2012-08-14T15:30:11.323 回答
0

在您的示例中,您需要在“add”方法中进行类型擦除,因此您不应在类中使用通配符。

通配符仅在您不需要类型擦除时使用(即,您不关心类型,只要它是.. 的子类)以及当您需要对泛型本身进行子类型化时。

于 2012-08-14T15:36:03.640 回答
0

通配符只是意味着它将是满足该标准的某个类。所以 ?extends AnInterface 意味着它将是一个(也是唯一一个)扩展 AnInterface 的类。

所以它可能是:

XClass<Impl1>
XClass<Impl2>

ETC...

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

以列表为例。可能会这样声明:

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));
于 2012-08-14T15:40:29.947 回答