为什么设置<!-?允许扩展Foo <?->>,但不允许Set <foo <?>>

时间:2019-01-09 08:23:57

标签: java generics

我想知道泛型在这种情况下如何工作以及为什么 允许使用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> {}

4 个答案:

答案 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