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