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

Предположим, класс B наследует от класса A, Следующее является законной Java:

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

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

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

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

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

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

2 ответа

Решение

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

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

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

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

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

Соотношение содержимого определено в §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,

Что значит присвоение списка на самом деле значит?

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

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)):

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

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


... Но что это значит?

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

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

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

Другие вопросы по тегам