我对某些事感到困惑。
我有一个不是集合的类,但它确实引用了通用对象:
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作为通用参数有什么问题吗?
答案 0 :(得分:6)
您可以添加A
和B
类型对象的原因是您使用接口对XClass进行了参数化,因此添加两个实现的不同类没有任何问题那个界面。
另一方面,如果您将XClass定义为:
XClass<A> xc = new XClass<A>();
然后表达式xc.add(b);
会产生编译错误,因为添加的所有对象必须与声明的类型相同,在本例中为A.
如果您将xc
声明为,例如:
XClass<? extends AnInterface> xc = new XClass<AnInterface>();
然后再添加a
或b
是不合法的,因为我们唯一知道的是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
的课程,ClassOne
或ClassTw
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));