鉴于此:
给定这个:
Set<?> set1 = new HashSet<Great>();
如果你能理解无界通配符的含义,那么这个方法就会起作用。无界通配符上的类型槽与extends
进行比较,因此如果你明确地这样做。
Set<? extends Object> set1 = new HashSet<Great>();
阅读是扩展对象吗?是的,所以它可以编译。
然后给出以下内容:
Set<Class<Great>> set3 = new HashSet<Class<Great>>();
关于为什么能够工作,如果您提取Set和HashSet的参数,它们都是Class<Great>
,这两个Class<Great>
正好是相同类型。
在没有通配符的情况下,类型将直接逐字比较。
如果我们编写set3以接受协变类型(这将被编译):
Set<? extends Class<Great>> set3a = new HashSet<Class<Great>>();
阅读时,HashSet的
Class<Great>
类型是否与Set的
Class<Great>
协变或兼容?是的,它是。因此它可以编译。
当然,如果它们只是完全相同的类型,没有人会写那种变量声明,这是多余的。通配符被编译器用来确定右侧赋值的泛型的具体类或接口参数是否与左侧泛型的具体/接口(理想情况下是接口,如下所示)兼容。
List<? extends Set<Great>> b = new ArrayList<HashSet<Great>>();
阅读它,
HashSet<Great>
是否协变为
Set<Great>
?是的。因此它可以编译。
所以让我们回到你的代码场景:
Set<Class<?>> set3 = new HashSet<Class<Object>>();
在这种情况下,同样的规则适用,您需要从最内层开始阅读,Object 是否兼容于通配符?是的。然后,您转向下一个最外层类型,它没有通配符。因此,在没有通配符的情况下,编译器将在
Class<Object>
和
Class<?>
之间进行逐字检查,它们不相等,因此会出现编译错误。
如果最外层有通配符,那么就可以编译。所以,您可能的意思是这个:
这是可以编译的:
Set<? extends Class<?>> singletonSet = new HashSet<Class<Object>>();
让我们举一个更加生动的例子,使用接口(类是具体类型),比如 Set。这段代码可以编译通过:
List<? extends Set<?>> b = new ArrayList<HashSet<Object>>();
请从内部向外部阅读,找出代码为什么能够编译,并进行显式操作:
最内层:对象是否兼容于 ? Extends Object
?当然是。
最外层:是否兼容于 ? extends Set<? extends Object>
?当然是。
对于第一个问题,下面的代码可以编译:
Set<? extends Object> hmm = new HashSet<Object>()
在第二点中,这是指(编译):
List<? extends Set<? extends Object>> b = new ArrayList<HashSet<Object>>()
现在让我们试着去掉最外层通配符,编译器将不会进行类型兼容/协变检查,现在事情将会被直接比较。因此,你现在知道以下问题的答案,这些代码是否可以编译?
List<Set<?>> b = new ArrayList<HashSet<Object>>();
// this is same as above:
List<Set<? extends Object>> b = new ArrayList<HashSet<Object>>();
你已经猜到了,没错...它将无法编译 :-)
要纠正上述问题,请执行以下任一操作:
List<? extends Set<? extends Object>> b = new ArrayList<HashSet<Object>>();
List<? extends Set<?>> b = new ArrayList<HashSet<Object>>();
然后,要纠正您的代码,请执行以下操作之一:
Set<? extends Class<? extends Object>> singletonSet =
new HashSet<Class<Object>>();
Set<? extends Class<?>> singletonSet = new HashSet<Class<Object>>();