【发布时间】:2016-11-14 22:34:25
【问题描述】:
我有一些返回通配符参数化类型的代码。我正在尝试将其传递给参数方法,但出现编译器错误。有人可以向我解释为什么类型不匹配,以及解决此问题的最佳方法是什么?
static <L extends List<T>,T extends Number>
void useList(List<L> list) {}
public static void main(String[] args) {
List<List<? extends Number>> list = null;
useList(list);
}
编译错误:
demo/GenericsHell.java:75: error: method useList in class GenericsHell cannot be applied to given types;
useList(list);
^
required: List<L>
found: List<List<? extends Number>>
reason: inference variable L has incompatible bounds
equality constraints: List<? extends Number>
upper bounds: List<CAP#1>
where L,T are type-variables:
L extends List<T> declared in method <L,T>useList(List<L>)
T extends Number declared in method <L,T>useList(List<L>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
1 error
在我的真实代码中,list 是由一个复杂的方法生成的,该方法可以返回多种类型的“列表”(实际上是一个自定义泛型类)。是否有另一种方法来参数化 useList 函数,以便它以类型安全的方式接受它?
编辑 1
我正在尝试将一大段代码简化为一个简洁、连贯的问题。从答案中我可以看到上面的代码过度简化了我遇到的确切问题。我将尝试用一个更复杂的例子来重申,同时更好地指定我的约束。
首先,我实际上并没有使用 List,而是使用双参数化的更复杂的类。我想不出任何标准类可以做到这一点,所以我将定义一个用于示例:
static class NumberLists<L extends List<T>,T extends Number> extends ArrayList<L> {}
我会避免多次重复使用一个类型,这样更容易保持水平。 useList() 方法确实需要双重参数化,因为它在内部使用这两种类型:
static <L extends List<T>,T extends Number>
Set<NumberLists<L,T>> useList(L list) {
NumberLists<L,T> nl = new NumberLists<L, T>();
nl.add(list);
return Collections.singleton(nl);
}
只要你有一个具体的类,这个框架就可以很好地工作:
// Everything works with a concrete class
List<Integer> intList = new ArrayList<Integer>();
// Use with parametric functions
Set<NumberLists<List<Integer>,Integer>> concreteSet = useList(intList);
// Access & use elements
NumberLists<List<Integer>,Integer> concreteNL = concreteSet.iterator().next();
concreteNL.add(intList);
问题是我有一个输入列表可以是几种不同的类型:
static List<? extends Number> makeList() {
if(Math.random()<.5) {
return new ArrayList<Integer>();
} else {
return new ArrayList<Double>();
}
}
List<? extends Number> numList = makeList();
这打破了上述许多可能的模式。
// What is the type here? This is obviously an error
Set<NumberLists<? extends List<? extends Number>>,? extends Number> paramSet = useList(numList);
// Type should ideally be compatible with numList still
NumberLists<?,?> paraNL1 = paramSet.iterator().next();
// Captures don't match
paraNL1.add(numList);
所以问题是我在编译时无法知道 numList 的具体类。而且我不觉得我在做任何实际上类型不安全的事情,因为我知道 numList 的类型必须与 useList 等返回的类型相匹配。
我确实可以控制大部分代码的类型签名。但是,我更喜欢以下内容:
- 它应该能够以类型安全的方式与具体类(例如 intList)很好地工作
- 它还将接收在运行时之前无法明确知道其类型的输入
- 如果执行强制转换或其他未经检查的操作,它们应该发生在输入构造附近。应检查以下操作。
有关完整(但非编译)的 java 文件,请参阅https://gist.github.com/sbliven/f7babb729e0b1bee8d2dabe5ee979431。由于我对这个问题感兴趣,请参阅https://github.com/biojava/biojava/issues/354。
【问题讨论】:
-
在我看来,您的列表包含一个列表,而
useList()期望该列表包含Number,而不是List。 -
@markspace 在顶部代码块中,我希望让 L 绑定 List。我认为这是正确的级别数。
-
我已更新问题并避免使用嵌套列表。希望这可以减少类型签名的混乱。
标签: java generics parametric-polymorphism