Ban*_*non 6 java generics casting wildcard lower-bound
我有一个通用接口interface ListList<E> extends List<List<E>>
。由于某些原因,我无法转换ListList<? super T>
为List<List<? super T>>
。有什么办法可以做到,为什么不起作用?
至此,我已经尝试了以下方法:
ListList<? super T>
给List<? extends List<? super T>>
(1),但是当我尝试分配ListList<? super T>
给List<List<? super T>>
我Incompatible types
时,出现编译时错误(1.1)。Incompatible types
编译时错误(2),它不起作用。ListList
,它可以正常工作(3),但我不喜欢原始类型。ListList<? super T>
到中List<? extends List<? super T>>
,它可以正常工作(4),但是我需要一个更通用的解决方案,该解决方案不仅适用于ListList<E>
,而且适用于任何通用类型。这是我的代码:
ListList<? super T> var = new ArrayListList<>();
List<? extends List<? super T>> work = var; // (1)
List<List<? super T>> notWork = var; // (1.1)
List<List<? super T>> explicit = (List<List<? super T>>) var; // (2)
List<List<? super T>> raw = (ListList) var; // (3)
List<List<? super T>> copy = new ArrayList<>(); // (4)
copy.addAll(var); // (4)
Run Code Online (Sandbox Code Playgroud)
我曾期望ListList<? super T>
如此List<List<? super T>>
,但似乎如此List<? extends List<? super T>>
。我需要知道为什么会这样,以及如何在List<List<? super T>>
没有原始类型和元素复制的情况下将其强制转换为。
乍一看,这些分配似乎都应该成功,但由于内部通配符的原因,它们没有成功? super T
。如果我们删除这些通配符,那么所有的赋值都会编译。
public static <T> void test() {
ListList<T> var = new ArrayListList<>();
List<? extends List<T>> work = var; // Compiles
List<List<T>> notWork = var; // Compiles
List<List<T>> explicit = (List<List<T>>) var; // Compiles
List<List<T>> raw = (ListList) var; // Compiles with warning
List<List<T>> copy = new ArrayList<>(); // Compiles
copy.addAll(var); // Compiles
}
Run Code Online (Sandbox Code Playgroud)
我仍然收到 (3) 的未经检查的转换警告,但它们仍然可以编译。
乍一看好像是声明接口
ListList<E> extends List<List<E>>
Run Code Online (Sandbox Code Playgroud)
使 aListList
等于sList
的a List
。但是,您所做的是采用嵌套类型参数并将其设为主要类型参数。造成差异的原因是嵌套通配符不执行通配符捕获。
这里的嵌套通配符意味着“与绑定匹配的任何类型的列表的列表”,但是这里的主级通配符意味着“与绑定匹配的特定但未知类型的“列表列表””。
人们无法将作为下界超类型的对象添加到集合中,因为类型参数(一种特定但未知的类型)可能是实际的界限。
List<? super Integer> test2 = new ArrayList<>();
test2.add(2); // Compiles; can add 2 if type parameter is Integer, Number, or Object
test2.add((Number) 2); // Error - Can't add Number to what could be Integer
test2.add(new Object()); // Error - Can't add Object to what could be Integer
Run Code Online (Sandbox Code Playgroud)
由于Java的泛型是不变的,当涉及到类型参数时,类型必须完全匹配,因此类似的情况都ListList
无法编译。
// My assumption of how your ArrayListList is defined.
class ArrayListList<E> extends ArrayList<List<E>> implements ListList<E> {}
ListList<? super Integer> llOfSuperI = new ArrayListList<>();
llOfSuperI.add(new ArrayList<Integer>()); // capture fails to match Integer
llOfSuperI.add(new ArrayList<Number>()); // capture fails to match Number
llOfSuperI.add(new ArrayList<Object>()); // capture fails to match Object
Run Code Online (Sandbox Code Playgroud)
然而,a List
ofList
可以对所有 3 种情况进行编译。
List<List<? super Integer>> lOfLOfSuperI = new ArrayList<>();
lOfLOfSuperI.add(new ArrayList<Integer>()); // no capture; compiles
lOfLOfSuperI.add(new ArrayList<Number>()); // no capture; compiles
lOfLOfSuperI.add(new ArrayList<Object>()); // no capture; compiles
Run Code Online (Sandbox Code Playgroud)
Your是与 a of sListList
不同的类型,但是定义类型参数的不同泛型行为意味着存在不同的泛型行为。这就是为什么你不能直接将 a 赋值给 a (1.1),也是为什么你不能强制转换它 (2)。您可以强制转换为原始类型以使其进行编译 (3),但这引入了将来使用强制转换对象的可能性;这就是警告的内容。您可以将其分配给(1),引入另一个通配符来捕获子类型关系,但这会引入要捕获的通配符;您将无法向该列表添加任何有用的内容。List
List
ListList<? super T>
List<List<? super T>>
ClassCastException
List<? extends List<? super T>>
这些差异的出现只是因为通配符引入了通配符捕获和相关差异。如果不使用通配符,aListList<E>
相当于 a,List<List<E>>
并且如本答案顶部所示,显示编译代码没有问题。
如果您希望所有子列表都使用相同的确切类型参数,请继续使用您的ListList
接口,但不要使用任何通配符。这会强制添加到 的所有列表使用完全相同的类型参数ListList
,即 aListList<Integer>
只能保存List<Integer>
s。
如果您希望所有子列表都简单地匹配通配符,例如在同一列表中包含List<Number>
, List<Integer>
, 和,则只需使用 a来避免通配符捕获。List<Object>
List<List<? super T>>
归档时间: |
|
查看次数: |
139 次 |
最近记录: |