Где спецификация Java говорит, что List <T> назначает List <? супер T>?

Предположим, что класс B наследует от класса A. Ниже приведена юридическая Java:

List<A> x;
List<? super B> y = x;

В терминах спецификации это означает, что List<A> присваивает значение List<? super B>. Однако мне трудно найти часть спецификации, которая говорит, что это законно. В частности, я считаю, что мы должны иметь отношение подтипа

List<A>  <:  List<? super B>

но раздел 4.10 спецификации Java 8 определяет отношение подтипа как транзитивное замыкание прямого отношения супертипа S >1 T и определяет прямое отношение супертипа в терминах конечной функции, которая вычисляет набор супертипов T. Не существует ограниченной функции, которая на входе List<A> может производить List<? super B>, так как может быть произвольное число B, которое наследуется от A, поэтому определение подтипа spec, похоже, разбивается на суперсимволы. В разделе 4.10.2 "Подтипирование между классами и типами интерфейсов" упоминаются подстановочные знаки, но он обрабатывает только другое направление, в котором подстановочный знак появляется в потенциальном подтипе (это направление вписывается в вычисленный механизм прямого супертипа).

Вопрос. Какая часть спецификации говорит, что вышеуказанный код является законным?

Мотивация - это код компилятора, поэтому этого недостаточно, чтобы понять, почему это законно интуитивно или придумать алгоритм, который его обрабатывает. Поскольку общая проблема подтипирования в Java неразрешима, я хотел бы обрабатывать точно те же случаи, что и спецификация, и поэтому хочу, чтобы часть спецификации обрабатывала этот случай.

Ответы

Ответ 1

List<? super B> определяется как супертип List<A> & sect; 4.10.2. Подтипирование между классами и типами интерфейсов:

Прямые супертипы параметризованного типа C<T1,...,Tn>, где Ti(1 ≤ я ≤ n) является типом, являются следующими:

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

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

Пусть C<T1,...,Tn> = List<A> и C<S1,...,Sn> = List<? super B>. Согласно второй пуле, List<? super B> является супертипом List<A>, если ? super B содержит A.

Соотношение contains определено в & sect; 4.5.1. Типы аргументов и подстановочные знаки:

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

  • ? extends T <= ? extends S если T <: S

  • ? super T <= ? super S, если S <: T

  • T <= T

  • T <= ? extends T

  • T <= ? super T

Во второй паре мы видим, что ? super B содержит ? super A. По последней пуле мы видим, что ? super A содержит A. Таким образом, транзитивно мы знаем, что ? super B содержит A.

Ответ 2

Что назначает список <? супер B > на самом деле означает?

Рассмотрим следующую программу:

public class Generics {
    static class Quux { }
    static class Foo extends Quux { }
    static class Bar extends Foo { }

    public static void main(String... args) {
        List<Foo> fooList = new ArrayList<>();
        // This is legal Java
        List<? super Bar> superBarList = fooList;
        // So is this
        List<? super Foo> superFooList = fooList;

        // However, this is *not* legal Java
        superBarList.add(new Quux());

        // Neither is this
        superFooList.add(new Quux());

        // Or this:
        superFooList.add(new Object());

        // But this is fine
        superFooList.add(new Foo());
    }
}

Зачем это было? Прежде всего, расскажите о том, что говорит JLS.

Из JLS, §4.5.1:

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

  • ? супер T <= & alpha; супер S, если S <: T
  • T < =? super T

Следовательно, T < =? супер S, если S <: T.


... но что это означает?

Если я не могу добавить new Quux() или new Object()? List<? super Foo> означает, что этот список содержит только те элементы, которые являются строгими супертипами до Foo, но Я не знаю, какой тип будет. Другими словами, я могу объявить список таким типом, но я не могу добавить к нему элементы, которые я не на 100% определен, имеют тип ? super Foo. Quux может быть этим типом, но также может быть не таким.

По этой причине назначение List<Foo> для List<? super Bar> не допускает загрязнения кучи и в конечном итоге не является проблемой.

Дальнейшее чтение: Соответствующая секция общего объяснения AngelikaLanger