泛型方法上的多个通配符使Java编译器(和我!)非常困惑
让我们首先考虑一个简单的场景(参见 ideone.com 的完整源代码):
import java.util.*;
public class TwoListsOfUnknowns {
    static void doNothing(List<?> list1, List<?> list2) { }
    public static void main(String[] args) {
        List<String> list1 = null;
        List<Integer> list2 = null;
        doNothing(list1, list2); // compiles fine!
    }
}
这两个通配符是不相关的,这就是为什么您可以使用 a 和 a 进行调用的原因。换句话说,两者可以指完全不同的类型。因此,以下内容不会编译,这是可以预料的(也是在 ideone.com):doNothingList<String>List<Integer>?
import java.util.*;
public class TwoListsOfUnknowns2 {
    static void doSomethingIllegal(List<?> list1, List<?> list2) {
        list1.addAll(list2); // DOES NOT COMPILE!!!
            // The method addAll(Collection<? extends capture#1-of ?>)
            // in the type List<capture#1-of ?> is not applicable for
            // the arguments (List<capture#2-of ?>)
    }
}
到目前为止,一切都很好,但这就是事情开始变得非常混乱的地方(如 ideone.com 所示):
import java.util.*;
public class LOLUnknowns1 {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }
}
上面的代码在Eclipse中为我编译,ideone.com,但是应该吗?难道我们不可能有 a 和 a,从中类似的两个不相关的通配符情况?sun-jdk-1.6.0.17List<List<Integer>> lolList<String> listTwoListsOfUnknowns
事实上,以下对该方向的轻微修改不会编译,这是可以预料的(如 ideone.com 所示):
import java.util.*;
public class LOLUnknowns2 {
    static void rightfullyIllegal(
            List<List<? extends Number>> lol, List<?> list) {
        lol.add(list); // DOES NOT COMPILE! As expected!!!
            // The method add(List<? extends Number>) in the type
            // List<List<? extends Number>> is not applicable for
            // the arguments (List<capture#1-of ?>)
    }
}
所以看起来编译器正在做它的工作,但后来我们得到了这个(如 ideone.com 所示):
import java.util.*;
public class LOLUnknowns3 {
    static void probablyIllegalAgain(
            List<List<? extends Number>> lol, List<? extends Number> list) {
        lol.add(list); // compiles fine!!! how come???
    }
}
同样,我们可能有例如a和a,所以这不应该编译,对吧?List<List<Integer>> lolList<Float> list
事实上,让我们回到更简单的(两个无界通配符),并尝试看看我们是否真的可以以任何方式调用。让我们先尝试“简单”的情况,并为两个通配符选择相同的类型(如 ideone.com 所示):LOLUnknowns1probablyIllegal
import java.util.*;
public class LOLUnknowns1a {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }
    public static void main(String[] args) {
        List<List<String>> lol = null;
        List<String> list = null;
        probablyIllegal(lol, list); // DOES NOT COMPILE!!
            // The method probablyIllegal(List<List<?>>, List<?>)
            // in the type LOLUnknowns1a is not applicable for the
            // arguments (List<List<String>>, List<String>)
    }
}
这是没有道理的!在这里,我们甚至没有尝试使用两种不同的类型,它也不会编译!使它成为一个,也给出了类似的编译错误!事实上,从我的实验来看,代码编译的唯一方法是第一个参数是显式类型(如 ideone.com 所示):List<List<Integer>> lolList<String> listnull
import java.util.*;
public class LOLUnknowns1b {
    static void probablyIllegal(List<List<?>> lol, List<?> list) {
        lol.add(list); // this compiles!! how come???
    }
    public static void main(String[] args) {
        List<String> list = null;
        probablyIllegal(null, list); // compiles fine!
            // throws NullPointerException at run-time
    }
}
因此,问题是关于 、 和 :LOLUnknowns1LOLUnknowns1aLOLUnknowns1b
- 哪些类型的参数可以接受?probablyIllegal
- 应该编译吗?它是类型安全的吗?lol.add(list);
- 这是编译器错误,还是我误解了通配符的捕获转换规则?
附录A:双重 LOL?
如果有人好奇,这可以编译得很好(如 ideone.com 所示):
import java.util.*;
public class DoubleLOL {
    static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
        // compiles just fine!!!
        lol1.addAll(lol2);
        lol2.addAll(lol1);
    }
}
附录 B:嵌套通配符 - 它们的真正含义是什么???
进一步的调查表明,也许多个通配符与问题无关,而是嵌套通配符是混淆的根源。
import java.util.*;
public class IntoTheWild {
    public static void main(String[] args) {
        List<?> list = new ArrayList<String>(); // compiles fine!
        List<List<?>> lol = new ArrayList<List<String>>(); // DOES NOT COMPILE!!!
            // Type mismatch: cannot convert from
            // ArrayList<List<String>> to List<List<?>>
    }
}
所以它看起来也许a不是一个.事实上,虽然 any 是 a ,但它看起来不像 any 是 a (如 ideone.com 所示):List<List<String>>List<List<?>>List<E>List<?>List<List<E>>List<List<?>>
import java.util.*;
public class IntoTheWild2 {
    static <E> List<?> makeItWild(List<E> list) {
        return list; // compiles fine!
    }
    static <E> List<List<?>> makeItWildLOL(List<List<E>> lol) {
        return lol;  // DOES NOT COMPILE!!!
            // Type mismatch: cannot convert from
            // List<List<E>> to List<List<?>>
    }
}
那么,一个新的问题就出现了:什么是?List<List<?>>
 
					 
				 
				    		 
				    		 
				    		 
				    		