Java两级参数化类型推理
我有一些返回通配符参数化类型的代码。我试图将其传递给参数化方法,但我得到了一个编译器错误。有人能给我解释一下为什么这些类型不匹配,以及解决这个问题的最佳方法是什么Java两级参数化类型推理,java,generics,parametric-polymorphism,Java,Generics,Parametric Polymorphism,我有一些返回通配符参数化类型的代码。我试图将其传递给参数化方法,但我得到了一个编译器错误。有人能给我解释一下为什么这些类型不匹配,以及解决这个问题的最佳方法是什么 static <L extends List<T>,T extends Number> void useList(List<L> list) {} public static void main(String[] args) { List<List<? extends Numb
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);
}
我将避免重复使用一个类型不止一次,因此更容易保持级别的正确性。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);
}
静态
设置使用列表(L列表){
NumberLists nl=新的NumberLists();
nl.添加(列表);
返回集合。单例(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);
//一切都与一个具体的类一起工作
List intList=new ArrayList();
//与参数化函数一起使用
Set-concreteSet=useList(intList);
//访问和使用元素
NumberLists concreteNL=concreteSet.iterator().next();
混凝土添加(intList);
问题是我有一个输入列表,可以是几种不同的类型:
static List<? extends Number> makeList() {
if(Math.random()<.5) {
return new ArrayList<Integer>();
} else {
return new ArrayList<Double>();
}
}
List<? extends Number> numList = makeList();
static List=paraNL1=paramSet.iterator().next();
//捕获不匹配
添加(numList);
所以问题是我无法在编译时知道numList的具体类。我并不觉得我在做任何类型不安全的事情,因为我知道numList的类型必须与useList返回的类型匹配,等等
我可以控制大部分代码的类型签名。然而,我更喜欢以下几点:
- 它应该以一种类型安全的方式与具体的类(例如intList)一起很好地工作
- 它还将接收直到运行时才能明确知道其类型的输入
- 如果执行了强制转换或其他未检查的操作,则这些操作应发生在输入的构造附近。应检查以下操作
有关完整(但非编译)的java文件,请参阅。由于我对这个问题感兴趣,请参见。问题在于编译器想要为
T
指定一个具体类型。在您的声明中,T
是否需要变成?根据列表
的声明扩展编号
,该声明不具体
您可以将useList
更改为以下声明之一,编译器会很高兴:
static <L extends List<? extends Number>> void useList(List<L> list) {
}
(此处T
将变为Number
)
是否可以替换?扩展数字
在变量声明中,一切都会很好:
public static <T extends Number> void main(String[] args) {
List<List<T>> list = null;
useList(list);
}
publicstaticvoidmain(字符串[]args){
List=null;
使用列表(列表);
}
对于第一个编译问题,您的问题来自于您试图将其分配给某个类型不匹配的变量
最简单的方法是从useList()
调用开始,以确保此简单代码编译:
List<? extends Number> numList = makeList();
useList(numList);
您看到您正试图使用paraNL1
作为消费者(它使用numList
),并告诉您必须使用super
声明它才能工作
事实上,编译器知道必须存在某种类型的扩展列表。在我看来,您的列表包含一个列表,
useList()
希望列表包含Number
,而不是顶部代码块中的list
@markspace,我希望让L绑定列表我已经更新了问题并避免使用嵌套列表。我想这是朝着我想要的方向迈出的一步,但是这些选项似乎都不适合我。我已经编辑了我的原始问题,以便更清楚地解释我的限制。我想我需要一个更好的IDE–EclipseLuna建议的Set
。那么,有没有什么方法可以使用super重新定义类,这样代码就可以工作了?我不确定这在我的真实代码中是否可行,但它可能会给我一些想法。Luna现在已经2岁多了。类型推断在Java8中发生了很大的变化,因此IDE中的支持有了很大的改进。最简单的编译方法是避免使用通配符。可能使用。@Quantum7我添加了一个带有通配符捕获辅助方法的示例。通配符捕获是我缺少的技术。谢谢
static <L extends List<? extends Number>> void useList(List<L> list) {
}
static <L extends List<? extends T>, T extends Number> void useList(List<L> list) {
}
public static <T extends Number> void main(String[] args) {
List<List<T>> list = null;
useList(list);
}
List<? extends Number> numList = makeList();
useList(numList);
Set<? extends NumberLists<? extends List<? extends Number>, ? extends Number>> paramSet =
useList(numList);
NumberLists<? extends List<? extends Number>, ? extends Number> paraNL1 =
paramSet.iterator().next();
paraNL1.add(numList); // will still not compile
private static <T extends Number, L extends List<T>> void helper(L numList) {
Set<NumberLists<L, T>> paramSet = useList(numList);
NumberLists<L, T> paraNL1 = paramSet.iterator().next();
paraNL1.add(numList);
}
List<? extends Number> numList = makeList();
helper(numList);