Где спецификация 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)):
Во второй паре мы видим, что ? 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