我想知道泛型在这种情况下如何工作以及为什么
允许使用Set<? extends Foo<?>> set3 = set1;
但不允许使用Set<Foo<?>> set2 = set1;
?
import java.util.HashSet;
import java.util.Set;
public class TestGenerics {
public static <T> void test() {
Set<T> set1 = new HashSet<>();
Set<?> set2 = set1; // OK
}
public static <T> void test2() {
Set<Foo<T>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // COMPILATION ERROR
Set<? extends Foo<?>> set3 = set1; // OK
}
}
class Foo<T> {}
答案 0 :(得分:8)
简单地说,这是因为Set<? extends Foo<?>>
是协变的(与extends
关键字)。协变类型是只读的,编译器将拒绝任何写操作,例如Set.add(..)
。
Set<Foo<?>>
不是协变的。它不会阻止写入或读取操作。
这个...
Set<Foo<String>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // KO by compiler
...是非法的,因为否则我可以例如通过Foo<Integer>
将set1
放入set2
。
set2.add(new Foo<Integer>()); // Whoopsie
但是...
Set<Foo<String>> set1 = new HashSet<>();
Set<? extends Foo<?>> set3 = set1; // OK
...是协变的(extends
关键字),因此是合法的。例如,编译器将拒绝诸如set3.add(new Foo<Integer>())
之类的写操作,但接受诸如set3.iterator()
之类的读操作。
Iterator<Foo<String>> fooIterator = set3.iterator(); // OK
set3.add(new Foo<String>()); // KO by compiler
有关更多说明,请参见以下帖子:
答案 1 :(得分:4)
如果将Foo的通用参数排除在等式之外,也许问题会变得更加清楚。
考虑
final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1;
这使编译错误更加明显。如果这是有效的,则有可能将对象插入set2,从而违反类型约束将其插入set1。
Set<? extends Foo> set3 = set1;
这是完全有效的,因为set1还将接受从Foo派生的类型。
答案 2 :(得分:1)
除了已经给出的答案外,我还将添加一些正式的解释。
由4.10.2(我的临时雇员)给出
给出一个通用类型声明C(n> 0),直接 参数化类型C的超类型,其中Ti(1≤i≤ n)是一种类型,是否所有以下内容:
D
,其中D是一个通用类型,它是 通用类型C的直接超类型,θ为 替换[F1:= T1,...,Fn:= Tn]。 C
,其中Si包含Ti(1≤i≤n)(第4.5.1节)。 对象类型,如果C是一个通用接口类型,没有 直接超级接口。
原始类型C。
contains
的规则在4.5.1中指定:
据说一个类型实参T1包含另一个类型实参T2, 如果由T2表示的类型集可证明是 自反和反身下由T1表示的类型集合的子集 下列规则的传递式闭合(其中<:表示子类型 (§4.10))
?扩展T <=?如果T <:S
则扩展S?扩展T <=?
?超级T <=?如果S <:T
则为超级S?超级T <=?
?超级T <=?扩展对象
T <= T
T <=吗?扩展T
T <=吗?超级T
从T <= ? super T <= ? extends Object = ?
开始,因此应用4.10.2 Foo<T> <: Foo<?>
,我们有了? extends Foo<T> <= ? extends Foo<?>
。但是Foo<T> <= ? extends Foo<T>
有Foo<T> <= ? extends Foo<?>
。
应用4.10.2,我们发现Set<? extends Foo<?>>
是Set<Foo<T>>
的直接超类型。
您的第一个示例为何无法编译的正式答案可以通过假设矛盾来获得。准确地:
如果Set<Foo<T>> <: Set<Foo<?>>
有Foo<T> <= Foo<?>
,则无法证明将自反或传递关系应用于4.5.1之后的规则。
答案 3 :(得分:0)
我认为这仅仅是因为<button>
<div>
<svg width="400" height="110">
<rect width="100%" height="100%" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
Sorry, your browser does not support inline SVG.
</svg>
</div>
</button>
元素数据类型不同,而除通用数据类型外它必须相同。
第一组button {
z-index: 1;
position: absolute;
top: calc(50% - 55px);
background-color: transparent;
border: none;
cursor: pointer;
}
数据类型为$('button').on('click', (e) => {
alert('clicked');
});
,
那么第二组 - name: 7. Wait for SSH to come up
local_action: wait_for
host={{ item.public_ip }}
port=22
state=started
with_items: "{{ ec2.instances }}"
是Set
,
如我所见,元素数据类型与Set<Foo<T>>
不同,而不是通用类型,因为它使用Foo<T>
,因此会导致编译错误。
与下面的无效不同数据类型示例相同:
Set<Foo<?>>
Foo<?>
之所以可以是因为它具有通用的Foo<T> != Foo<?>
,并且可以接受任何数据类型。
例如:
Foo