通用方法上的多个通配符使Java编译器(和我!)非常困惑 让我们先考虑一个简单的场景():
到目前为止还不错,但事情开始变得非常混乱(): 看起来编译器正在完成它的工作,但是我们得到了这个(): 这毫无意义!在这里,我们甚至没有尝试使用两种不同的类型,它也不会编译!将其设置为通用方法上的多个通配符使Java编译器(和我!)非常困惑 让我们先考虑一个简单的场景():,java,generics,wildcard,compiler-errors,Java,Generics,Wildcard,Compiler Errors,到目前为止还不错,但事情开始变得非常混乱(): 看起来编译器正在完成它的工作,但是我们得到了这个(): 这毫无意义!在这里,我们甚至没有尝试使用两种不同的类型,它也不会编译!将其设置为List lol和List List也会产生类似的编译错误!事实上,根据我的实验,代码编译的唯一方法是如果第一个参数是显式的nulltype(): 附录B:嵌套通配符——它们的真正含义是什么??? 进一步的调查表明,可能多个通配符与问题无关,但嵌套的通配符是混淆的根源 import java.util.*; p
List lol
和List List
也会产生类似的编译错误!事实上,根据我的实验,代码编译的唯一方法是如果第一个参数是显式的null
type():
附录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<?>>
}
}
import java.util.*;
进入野外的公共课{
公共静态void main(字符串[]args){
List List=new ArrayList();//编译很好!
列表>
}
}
所以它看起来可能是一个列表
不是一个列表
,它看起来不像任何列表
是一个列表makeItWild(列表列表){
返回列表;//编译很好!
}
静态列表>
}
}
于是,一个新的问题出现了:什么是列表
- 不应接受带有泛型的参数。在
LOLUnknowns1b
的情况下,null
被接受,就好像第一个参数被键入为List
。例如,它编译:
List lol = null;
List<String> list = null;
probablyIllegal(lol, list);
lol.add()
需要一个类型为List的参数,正如附录B所示,这与多个通配符无关,而是误解了什么List不是专家,但我想我能理解
让我们将您的示例更改为等效的示例,但使用更具区别的类型:
static void probablyIllegal(List<Class<?>> x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
静态无效概率非法(列表y){
x、 添加(y);//这是编译的!!怎么回事???
}
让我们将列表更改为[]以更具启发性:
static void probablyIllegal(Class<?>[] x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
静态无效概率非法(类[]x,类y){
x、 添加(y);//这是编译的!!怎么回事???
}
现在,x不是某种类型的类的数组。它是任何类型的类的数组。它可以包含一个类
和一个类
。这不能用普通类型参数表示:
static<T> void probablyIllegal(Class<T>[] x //homogeneous! not the same!
static void probablyIllegal(类[]x//同构!不一样!
Class
是任何T
的Class
的超级类型。如果我们认为一个类型是一组对象,那么setClass
就是所有T
的Class
集合的并集(它包括它自己吗?我不知道…)如果有人想深入了解常见问题:我现在就要开始了……好吧,我就在这个常见问题上。但即使这样,我也不明白为什么这不是一个bug。相关:我真的不知道我是否做得很好,但我已经试着解释为什么会是这样。所以基本上,这不是一个bug,而是一个避免在编译过程中浪费时间的功能?@Colin:找到“为什么它设计成这样?”的答案将是非常棒的。我现在正在寻找答案。+1这个解释证实了我的直觉,但比我的更具说服力:-)至于原因,我仍然觉得通用子类型保持不变是一个足够好的理由。@Colin:第19行没有什么特别之处,lol
和list
的类型完全相同。ListList从List
到T[]
不是有效的步骤,因为数组是协变的,泛型在Java中是不变的。也就是说,字符串[]
是一个对象[]
,但是列表
不是列表
。你说得对。但我只是想做一些类比来愚弄我们自己,让我们在直觉的层面上接受它。我不会去研究实际的类型理论。
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>)
}
}
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
}
}
import java.util.*;
public class DoubleLOL {
static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
// compiles just fine!!!
lol1.addAll(lol2);
lol2.addAll(lol1);
}
}
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<?>>
}
}
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 lol = null;
List<String> list = null;
probablyIllegal(lol, list);
static void probablyIllegalAgain(List<List<? extends Number>> lol, List<? extends Integer> list) {
lol.add(list); // compiles fine!!! how come???
}
List<List<?>> lolAny = new ArrayList<List<?>>();
lolAny.add(new ArrayList<Integer>());
lolAny.add(new ArrayList<String>());
// lolAny = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
List<? extends List<?>> lolSome;
lolSome = new ArrayList<List<String>>();
lolSome = new ArrayList<List<Integer>>();
List<List<? extends Number>> lolAnyNum = new ArrayList<List<? extends Number>>();
lolAnyNum.add(new ArrayList<Integer>());
lolAnyNum.add(new ArrayList<Float>());
// lolAnyNum.add(new ArrayList<String>()); // DOES NOT COMPILE!!
// lolAnyNum = new ArrayList<List<Integer>>(); // DOES NOT COMPILE!!
List<? extends List<? extends Number>> lolSomeNum;
lolSomeNum = new ArrayList<List<Integer>>();
lolSomeNum = new ArrayList<List<Float>>();
// lolSomeNum = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
public class LOLUnknowns1d {
static void nowDefinitelyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE!!!
// The method add(capture#1-of ? extends List<?>) in the
// type List<capture#1-of ? extends List<?>> is not
// applicable for the arguments (List<capture#3-of ?>)
}
public static void main(String[] args) {
List<Object> list = null;
List<List<String>> lolString = null;
List<List<Integer>> lolInteger = null;
// these casts are valid
nowDefinitelyIllegal(lolString, list);
nowDefinitelyIllegal(lolInteger, list);
}
}
// WildSnippet1
new HashMap<?,?>(); // DOES NOT COMPILE!!!
new HashMap<List<?>, ?>(); // DOES NOT COMPILE!!!
new HashMap<?, Set<?>>(); // DOES NOT COMPILE!!!
// WildSnippet2
new HashMap<List<?>,Set<?>>(); // compiles fine!
new HashMap<Map<?,?>, Map<?,Map<?,?>>>(); // compiles fine!
static void probablyIllegal(List<Class<?>> x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
static void probablyIllegal(Class<?>[] x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
static<T> void probablyIllegal(Class<T>[] x //homogeneous! not the same!