我正在寻找一种解决方案,允许我将任何内部类型的Deques添加到我的List中。现在可以编写以下代码了。
/*
* Please ignore the missing instantiations. It's only a technical
* precompiler demonstration.
*/
final Deque<String> concreteDeque = null;
final Deque<?> unclearDeque = null;
final List<Deque<?>> list = null;
/*
* The precompiler demands a parameter of type Deque<?>. That means any
* Deque. Great! :)
*/
list.add(concreteDeque); // no precompiler error
list.add(unclearDeque); // no precompiler error
final Deque<?> deque = list.get(0); // no precompiler error, great! :)现在,我想做一个更具体的界面。
/**
* A list that holds instances of {@link Deque}. Only for demonstrations.
*
* @author Barock
*
* @param <DT>
* The type that should pass to the {@link Deque}.
*/
public interface DequeList<DT> extends List<Deque<DT>> { }但是有了这个接口,我的代码就不能工作了。
final Deque<String> concreteDeque = null;
final Deque<?> unclearDeque = null;
final DequeList<?> dequeList = null;
// Now the precompiler announces wildcard capture for adding elements!?
dequeList.add(concreteDeque); // precompiler error
dequeList.add(unclearDeque); // precompiler error
final Deque<?> deque = dequeList.get(0); // still no precompiler error. :)我假设我的DequeList<?>等同于List<Deque<?>>。有何不可呢?
发布于 2017-12-16 21:25:44
List<?>和List<List<?>>是有区别的(你可以把任何泛型集合放在list的位置)。
List<?>专为只读目的而设计。假设List<?>已经包含了一个“某些类型”的元素。因为“-”太笼统了,你不能确定它是什么类型,所以你不能简单地在类型安全列表中添加任何东西,否则list将包含异类元素(不同类型)。
考虑下面的示例:
List<String> someLetters = Arrays.asList("a", "b", "c", "d", "e");
List<Integer> someInts = Arrays.asList(1, 2, 3, 4, 5);
displayContent(someLetters);
displayContent(someInts);
public static void displayContent(List<?> list) {
System.out.println(list);
}当这个列表被转换为List<?>时,在someLetters中添加一个Integer是否合法?也许在其他语言中这是可能的,但在Java中不是,因为指定类型的Collection 必须是同构的。注<?>指定了隐藏的“某些类型”,但这绝对不意味着您可以添加任何您想要的内容。在上面的示例中,方法displayContent不知道list的真实类型,因此任何添加都是非法的:
public static void displayContent(List<?> list) {
// illegal as it may break list's homogenity...
list.add(1);
// illegal as it may break list's homogenity...
list.add("f");
System.out.println(list);
}在List<List<?>>的情况下,你有一个由只读列表组成的列表,它允许你添加任何你想要的只读列表,但不允许你在从这个“列表列表”中获得的列表中添加一些东西,也就是说,由于上面提到的原因,你不能像这样做list.get(0).add(something);。
编辑
我不知道为什么DequeList<?>不等同于List<Deque<E>,但是我刚刚发现了另一件事,它可能对其他试图解开这个谜团的人有用。由于某些原因,您不能在DequeList<?>中添加某种类型的双头队列,即Deque<?>,但您可以在DequeList<?>中添加非类型的双头队列,即Deque
Deque<String> concreteDeque = null;
Deque<?> unclearDeque = null;
Deque rawDeque = null;
List<Deque<?>> list = null;
list.add(concreteDeque); // OK
list.add(unclearDeque); // OK
list.add(rawDeque); // OK
DequeList<?> myList = null; // should be equivalent to List<Deque<?>>
myList.add(concreteDeque); // ERROR
myList.add(unclearDeque); // ERROR
myList.add(rawDeque); // OK似乎在“扩展”之后,Deque<?>成为了一种强大的DequeList类型。由于DequeList可能包含特定类型的Deque,例如Deque<String>,所以您不能添加在<?>中指定或隐藏的任何其他类型的deques。不幸的是,它没有回答为什么可以添加rawDeque。
发布于 2017-12-16 22:25:10
我支持matoni的答案,因为他解释了大部分问题,但我认为他的解释是不完整的。
如果为True,则List<?>将拒绝任何泛型参数,特别是它将拒绝对add方法的任何调用,这将使它实际上是只读的。同样的事情也发生在DequeList<?>上,它也是只读的。
然而,人们可以很容易地看出,对List<Deque<?>>安全的东西对DequeList<?>也是安全的,反之亦然。编译器无法解决这个问题,这是为什么呢?
让我们尝试一下:如果我们尝试实例化我们的DequeList,并为它提供与您在第一个代码片段中使用的声明类型相同的声明类型,会发生什么?如果我们设法使用相同的类型声明If,编译器将允许我们对其执行相同的操作。
public static class DequeList<DT> implements List<Deque<DT>> {
// implement all methods with default implementations to make the compiler happy
}
// oups, doesn't compile: "Wildcard type '?' cannot be instantiated directly"
List<Deque<?>> dequeList = new DequeList<?>();好吧,它不起作用。显然,通配符只能用于更改现有泛型对象的声明类型,而不能用于实例化新对象。让我们尝试一下其他的东西:
DequeList<?> dequeList1 = null;
// again, doesn't compile: "Incompatible types. Required: List<java.util.Deque<?>>. Found: DequeList<capture<?>>"
List<Deque<?>> dequeList2 = dequeList1;因此,对于编译器来说,List<Deque<?>>和DequeList<?>似乎只是不兼容的类型。
我对此没有完整的解释,但这似乎是编译器的一个限制,它无法推断这些类型实际上是相同的,我不认为这是不应该编译的根本原因。
https://stackoverflow.com/questions/47811964
复制相似问题