Я заметил, что JLS говорит о 5.1.10 Capture Преобразование, но я не понимаю, что это такое.
Может ли кто-нибудь объяснить их мне/привести примеры?
Я заметил, что JLS говорит о 5.1.10 Capture Преобразование, но я не понимаю, что это такое.
Может ли кто-нибудь объяснить их мне/привести примеры?
Преобразование захвата было разработано, чтобы сделать подстановочные знаки (в обобщениях) ?
полезными.
Предположим, у нас есть следующий класс:
public interface Test<T> {
public void shout(T whatever);
public T repeatPreviousShout();
}
и где-то в нашем коде у нас есть,
public static void instantTest(Test<?> test) {
System.out.println(test.repeatPreviousShout());
}
Поскольку test
не является необработанным Test
, и поскольку repeatPreviousShout()
в «задним числом» возвращает ?
, компилятор знает, что есть T
, который служит параметром типа для Test
. Это T
для некоторого неизвестного T
, поэтому компилятор стирает неизвестный тип (для подстановочных знаков он заменяется на Object
), поэтому repeatPreviousShout()
возвращает Object
.
Но если бы у нас было,
public static void instantTest2(Test<?> test) {
test.shout(test.repeatPreviousShout());
}
Компилятор выдаст нам ошибку вроде Test<capture#xxx of ?> cannot be applied
(где xxx
— число, например 337
).
Это связано с тем, что компилятор пытается выполнить проверку безопасности типов для shout()
, но поскольку он получил подстановочный знак, он не знает, что представляет собой T
, поэтому создает заполнитель с именем захват.
Взято с здесь (Теория и практика Java: безумие с дженериками, часть 1), в нем четко сказано:
Преобразование захвата — это то, что позволяет компилятору создать имя типа-заполнителя для захваченного подстановочного знака, чтобы вывод типа мог сделать вывод, что это именно тот тип.
Надеюсь, это поможет вам.
Параметризованный тип, включающий аргументы подстановочного знака, на самом деле является типом объединения. Например
List<? extends Number> = Union{ List<S> | S <: Number }
В двух случаях вместо использования List<? extends Number>
Java использует захваченную версию List<S>
, где S — только что созданная переменная типа с верхней границей Number
.
(1) http://java.sun.com/docs/books/jls/Third_edition/html/expressions.html
Чтобы сузить тип выражения. Если тип выражения равен List<? extends Number>
, мы точно знаем, что тип объекта во время выполнения на самом деле является List<S>
для некоторого конкретного типа S (S <: Number>
). Поэтому компилятор вместо этого использует List<S>
для более точного анализа типов.
Преобразование захвата применяется к каждому выражению отдельно; это приводит к некоторым глупым результатам:
<T> void test1(List<T> a){}
<T> void test2(List<T> a, List<T> b){}
List<?> x = ...;
test1(x); // ok
test2(x, x); // error
(2) http://java.sun.com/docs/books/jls/Third_edition/html/typesValues.html#4.10.2
При проверке подтипа A :< B
, где A
включает аргументы с подстановочными знаками. Например,
List<? extends Number> :< B
<=>
Union{ List<S> | S <: Number} :< B
<=>
List<S> :< B, for all S <: Number
Таким образом, мы проверяем захваченную версию типа A
.