让我们首先考虑一个简单的场景(请参阅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!
}
}
这两个通配符是无关的,这就是为什么你可以doNothing使用a List<String>
和a 进行调用的原因List<Integer>
。换句话说,两者?可以指完全不同的类型。因此,以下内容无法编译,这是可以预期的(同样在ideone.com上):
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和sun-jdk-1.6.0.17ideone.com 上为我编译,但是可以吗?我们不可能有一个List<List<Integer>>
lol和一个List<String>
list类似的两个不相关的通配符情况TwoListsOfUnknowns吗?
实际上,朝该方向进行的以下轻微修改无法编译,这是可以预期的(如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.*;
html" target="_blank">public class LOLUnknowns3 {
static void probablyIllegalAgain(
List<List<? extends Number>> lol, List<? extends Number> list) {
lol.add(list); // compiles fine!!! how come???
}
}
同样,我们可能有例如a List<List<Integer>> lol
和a List<Float> list
,所以不应该编译,对吗?
实际上,让我们回到较简单的示例LOLUnknowns1(两个无界通配符),并尝试看看我们是否实际上可以probablyIllegal以任何方式调用。让我们先尝试“简单”情况,然后为两个通配符选择相同的类型(如ideone.com所示):
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>)
}
}
这没有道理!在这里,我们甚至没有尝试使用两种不同的类型,并且它不会编译!使其成为,List<List<Integer>> lol
并且List<String> list
还会给出类似的编译错误!实际上,根据我的实验,代码的唯一编译方式是第一个参数是显式null类型(如ideone.com所示):
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
}
}
所以,问题是,关于LOLUnknowns1,LOLUnknowns1a和LOLUnknowns1b:
probablyIllegal
接受哪些类型的参数?
应该lol.add(list)
;编译吗?它是类型安全的吗?
这是编译器错误还是我误解了通配符的捕获转换规则?
附录A:双重大声笑?
万一有人好奇,可以很好地编译(如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 List<List<String>>
不是List<List<?>>
。实际上,尽管any List<E>
是a List<?>
,但看起来并不像any List<List<E>>
是a List<List<?>>
(如ideone.com所示):
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<?>>
?
如附录B所示,这与多个通配符无关,而是误解了List<List<?>>真正的含义。
让我们首先提醒自己,Java泛型是不变的意味着什么:
Integer
is a Number
List<Integer>
is NOT a List<Number>
List<Integer>
IS a List<? extends Number>
现在,我们简单地将相同的参数应用于嵌套列表情况(有关更多详细信息,请参见附录):
List<String>
is (captureable by) a List<?>
List<List<String>>
is NOT (captureable by) a List<List<?>>
List<List<String>>
IS (captureable by) a List<? extends List<?>>
有了这种理解,就可以解释问题中的所有片段。所以产生了困惑中(错误地)认为像一个类型List<List<?>>
可以捕获类型,如List<List<String>>,List<List<Integer>>
等,这是不正确的。
即List<List<?>>
:
是不是一个列表,其元素是某一个未知类型的列表。
…那将是一个 List<? extends List<?>>
相反,它是一个列表,其元素是ANY类型的列表。
片段
以下是说明以上几点的代码段:
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!!
回到问题
回到问题中的代码片段,以下行为符合预期(如ideone.com所示):
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);
}
}
lol.add(list);
是非法的,因为我们可能有List<List<String>> lol
和List<Object> list
。实际上,如果我们注释掉有问题的语句,则代码会编译,而这正是我们在中首次调用时所拥有的main。
问题中的所有probablyIllegal
方法都不是非法的。它们都是完全合法和类型安全的。编译器中绝对没有错误。它确实在做应该做的事情。
问题内容: 我对Java泛型中的嵌套嵌套通配符有疑问。 这是一个常见的情况: 这是标准的Java泛型,工作正常。 但是,如果通配符嵌套,它将不再起作用: 这会导致编译器错误。 我已经尝试了各种强制类型转换和通配符置换,但是无法正常工作。我不记得以前曾见过此问题,并且我使用泛型已有多年。我是否太累了,缺少明显的东西? 问题答案: 所以问题是,可以实现为: 您需要确定您的实际意思。 大概是这样的:
我不明白为什么会出现这些编译错误: 1: 类型列表中的add(capture#1-of?extends Exec.Bird)方法不适用于参数(Exec.Sparrow) 2: 方法添加(捕获#2-of?扩展Exec.Bird)类型列表中的参数(Exec.鸟)
问题内容: 我正在从OracleDocGenericMethod中阅读有关泛型方法的信息。当比较指出何时使用通配符以及何时使用通用方法时,我对此感到非常困惑。引用文档。 我们可以在这里使用通用方法: […]这告诉我们类型参数正在用于多态。它的唯一作用是允许在不同的调用站点使用各种实际的参数类型。在这种情况下,应使用通配符。通配符旨在支持灵活的子类型化,这就是我们在此要表达的内容。 我们难道不认为像
我正在阅读OracleDocGenericmethod中的泛型方法。当它说什么时候使用通配符和什么时候使用泛型方法时,我对比较感到很困惑。引用文档。 我们本可以在这里使用通用方法: [...]这告诉我们类型参数正被用于多态;它的唯一作用是允许在不同的调用站点使用各种实际的参数类型。如果是这样的话,应该使用通配符。通配符旨在支持灵活的子类型,这就是我们在这里试图表达的。 我们不觉得像《代码》(Col
我得到以下编译错误: 当我编译(在Eclipse Juno中使用JDK 1.7.0)以下代码时: null 这样我就可以测试我所有的排序实现并测试它们。我想将结果与Java的排序实现进行比较,所以我也在编写这个接口的实现,它在内部只调用Java的排序方法。这就是我面对问题的地方。
问题内容: Logstash似乎不读取通配符的路径,这是我的配置文件 问题答案: 通过用反斜杠替换反斜杠,我能够解决此问题。