Java 通配符未按预期通过继承传递(意外的通配符捕获)

标签 java generics wildcard

我正在寻找一个允许我添加 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>> { }

但是有了这个接口(interface),我的代码就不能再工作了。

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<?>> .为什么显然不是?

最佳答案

List<?>List<List<?>> 之间存在差异(您可以放置​​任何通用集合来代替列表)。

List<?> 专为只读目的而设计。假设 List<?> 已经包含了一个“some-type”的元素。由于“some-type”过于笼统,你无法确定它是什么类型,因此你不能简单地添加任何东西,因为类型安全,否则列表将包含异构元素(不同类型).

考虑以下示例:

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

当此列表转换为 Integer 时,在 someLetters 中添加 List<?> 是否合法?也许在其他一些语言中这是可能的,但在 Java 中不行,因为指定类型的 Collection 必须是同质的。注意 <?> 指定了隐藏的“some-type”,但它绝对不意味着你可以添加任何你想要的东西。在上面的示例中,方法 displayContent 不知道列表的真实类型,因此任何添加都是非法的:

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> ,但是我刚刚发现了另一件事,这可能对其他试图解决这个谜团的人有用。由于某种原因,您不能添加某种类型 i 的双端队列。 e. Deque<?> in DequeList<?> ,但是你可以添加没有类型 i 的双端队列。 e. Deque 中的 DequeList<?> :

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 可能包含特定类型 e 的双端队列。 G。 Deque<String>,您不能添加 Deque 中指定或隐藏的任何其他类型的 <?>。不幸的是,它没有回答为什么可以添加 rawDeque

关于Java 通配符未按预期通过继承传递(意外的通配符捕获),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47811964/

相关文章:

java - 链接消费者 Java 8

java - 随机选择键值对的最佳方法

sql - 范围通配符模式匹配行为与区分大小写的排序规则

SSL 多级子域通配符

sql-server - 全文搜索 - 包含加通配符和单引号

java - 无法使用 maven pom 和 eclipse 一致地创建 src/test/java 模块 jar

java - 基于集合的泛型和反射

c++ - C++ 中的普通结构数组的通用 TableView/DataFrame 功能

c# - 在 Ninject 3.0 中绑定(bind)泛型类型

java - 比较两个包含日期但需要忽略格式的字符串类型列表