Почему Set <? расширяет Foo <? >> разрешено, но Set <Foo <? >> не разрешено

Я хочу знать, как дженерики работают в такой ситуации и почему Set<? extends Foo<?>> set3 = set1; 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> {}

Ответ 1

Проще говоря, это потому, что Set<? extends Foo<?>> 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

Смотрите эти посты для лучшего объяснения:

Ответ 2

Возможно, проблема станет яснее, если вы укажете общий параметр Foo вне уравнения.

Рассматривать

final Set<Foo> set1 = new HashSet<>();
Set<Object> set2 = set1;

Это делает ошибку компиляции более очевидной. Если бы это было допустимо, было бы возможно вставить объект в set2, таким образом в set1, нарушая ограничение типа.

Set<? extends Foo> set3 = set1;

Это совершенно верно, потому что set1 также будет принимать типы, полученные из Foo.

Ответ 3

В дополнение к уже приведенным ответам я добавлю некоторые формальные объяснения.

Дано 4.10.2 (emp. Mine)

При заданном объявлении универсального типа C (n> 0) непосредственными супертипами параметризованного типа C, где Ti (1 ≤ я ≤ n) является типом, являются все следующие:

D <U1 θ,..., Uk θ>, где D - универсальный тип, который является прямым супертипом универсального типа C, а θ - замена [F1: = T1,..., Fn: = Tn].

C <S1,..., Sn>, где Si содержит Ti (1 ≤ я ≤ n) (§4.5.1).

Тип Object, если C является универсальным типом интерфейса без прямых суперинтерфейсов.

Сырой тип C.

Правило contains указаны в 4.5.1:

Говорят, что аргумент типа T1 содержит другой аргумент типа T2, записанный как T2 <= T1, если набор типов, обозначаемых через T2, является доказуемо подмножеством набора типов, обозначаемых через T1 при рефлексивном и транзитивном замыкании следующих правил ( где <: обозначает подтип (§4.10)):

? расширяет T <=? расширяет S, если T <: S

? расширяет T <=?

? супер Т <=? супер S если S <: T

? супер Т <=?

? супер Т <=? расширяет объект

Т <= Т

Т <=? расширяет T

Т <=? супер т

Так как T <=? super T <=? extends Object =? T <=? super T <=? extends Object =? так что применение 4.10.2 Foo<T> <: Foo<?> у нас есть ? extends Foo<T> <=? extends Foo<?> ? extends Foo<T> <=? extends Foo<?> ? extends Foo<T> <=? extends Foo<?>. Но Foo<T> <=? extends Foo<T> Foo<T> <=? extends Foo<T> поэтому у нас есть Foo<T> <=? extends Foo<?> Foo<T> <=? extends Foo<?>.

Применяя 4.10.2, мы имеем этот Set<? extends Foo<?>> Set<? extends Foo<?>> является прямым супертипом Set<Foo<T>>.

Формальный ответ на вопрос, почему ваш первый пример не компилируется, можно получить, предположив противоречие. Percisely:

Если Set<Foo<T>> <: Set<Foo<?>> у нас есть тот Foo<T> <= Foo<?> Который невозможно доказать, применяя рефлексивные или транзитивные отношения к правилам из 4.5.1.

Ответ 4

Я думаю, просто потому, что элемент Datatype элемента Set отличается, хотя он должен быть таким же, за исключением Generic Datatype.
первый набор Set<Foo<T>> тип данных Foo<T>,
затем второй набор Set<Foo<?>> равен Foo<?>,
Как я вижу, тип данных элемента отличается от Foo<T> != Foo<?> И не является универсальным типом, потому что он использует Foo, поэтому может вызвать ошибку компиляции.
Это то же самое, что и ниже недопустимый пример другого типа данных:

Set<List<T>> set3 = new HashSet<>();
Set<List<?>> set4 = set3;   // compilation error due to different element datatype List<T> != List<?>

Set<? extends Foo<?>> set3 = set1; может потому что есть ? datatype ? datatype который является универсальным и имеет цель, может принимать любой тип данных.
пример:

Set<List<T>> set4 = new HashSet<>();
Set<?> set5 = set4;  // would be Ok