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

时间:2017-12-14 11:13:56

标签: 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>> { }

但是通过这个界面,我的代码不再适用了。

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

2 个答案:

答案 0 :(得分:2)

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

当此列表投放到Integer时,在someLetters中添加List<?>是否合法?也许在其他语言中它是可能的,但不是在Java中,因为指定类型的<{1}} 必须是同质的。注意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<?>,但是我刚刚发现了另一件可能对其他试图解决这个问题的人有用的事情。出于某种原因,你不能添加某种类型的deque。即List<Deque<E>中的Deque<?>,但您可以添加任何类型的deque i。即DequeList<?>中的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可能包含特定类型e的deques。 G。 DequeList,您无法添加Deque<String>中指定或隐藏的任何其他类型的Deque。不幸的是,它没有回答为什么可以添加<?>

答案 1 :(得分:0)

由于他解释了大部分问题,我已经对matoni的答案进行了评价,但我相信他的解释是不完整的。

是的,List<?>将拒绝任何通用参数,特别是它会拒绝对add方法的任何调用,这使得它实际上是只读的。完全相同的事情发生在DequeList<?>,它也是只读的。

然而,人类可以轻易地告诉List<Deque<?>>安全DequeList<?>也是安全的DequeList,反之亦然。编译器无法解决这个问题,为什么会这样?

让我们尝试一下:如果我们尝试实例化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<?>#include "IDs.h" #include <windows.h> DIALOG_HELP DIALOG CW_USEDEFAULT, CW_USEDEFAULT, 200, 120 STYLE WS_OVERLAPPEDWINDOW CAPTION "Title" BEGIN DEFPUSHBUTTON "Ok", IDOK, 96, 90, 42, 12 ICON icone1, -1, 60, 55, 32, 32 LTEXT "Test DialogBox", -1, 100, 58, 100, 10 END 只是编译器的不兼容类型。

我对此没有完整的解释,但似乎编译器的限制无法推断这些类型实际上是相同的,我不认为有一个根本原因不应该编译。