Где спецификация 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>
поскольку может быть произвольное число B
s, которые наследуют от 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
,
Что значит присвоение списка супер 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
Говорят, что аргумент типа 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