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

Sto*_*nev 21 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> {}
Run Code Online (Sandbox Code Playgroud)

kag*_*ole 8

简单地说,这是因为Set<? extends Foo<?>>是协变的(使用extends关键字).协变类型是只读的,编译器将拒绝任何写操作,如Set.add(..).

Set<Foo<?>>是不协变的.它不会阻止写入或读取操作.

这个...

Set<Foo<String>> set1 = new HashSet<>();
Set<Foo<?>> set2 = set1; // KO by compiler
Run Code Online (Sandbox Code Playgroud)

...是非法的,因为否则我可能例如把Foo<Integer>set1通过set2.

set2.add(new Foo<Integer>()); // Whoopsie
Run Code Online (Sandbox Code Playgroud)

但...

Set<Foo<String>> set1 = new HashSet<>();
Set<? extends Foo<?>> set3 = set1; // OK
Run Code Online (Sandbox Code Playgroud)

...是协变(extends关键字),所以它是合法的.例如,编译器将拒绝类似的写操作set3.add(new Foo<Integer>()),但接受类似的读操作set3.iterator().

Iterator<Foo<String>> fooIterator = set3.iterator(); // OK
set3.add(new Foo<String>()); // KO by compiler
Run Code Online (Sandbox Code Playgroud)

请参阅这些帖子以获得更好的解释:

  • 你的意思是`Foo <Integer> foo; set2.add(foo);`因为`set2.add(42)`42不是`Foo <?>`. (3认同)

lef*_*bit 4

如果您将 Foo 的通用参数排除在等式之外,也许问题会变得更清楚。

考虑

final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1;
Run Code Online (Sandbox Code Playgroud)

这使得编译错误更加明显。如果这是有效的,则可以将对象插入到 set2 中,从而违反类型约束插入到 set1 中。

Set<? extends Foo> set3 = set1;
Run Code Online (Sandbox Code Playgroud)

这是完全有效的,因为 set1 也接受从 Foo 派生的类型。

  • 您的回答还暗示“set3”是可写的,但事实并非如此。在这里查看有关协变和逆变的更多信息:/sf/answers/304048321/ (2认同)