Что такое преобразование захвата в Java и может ли кто-нибудь привести мне примеры?

Я заметил, что JLS говорит о 5.1.10 Capture Преобразование, но я не понимаю, что это такое.

Может ли кто-нибудь объяснить их мне/привести примеры?


person John Assymptoth    schedule 13.12.2010    source источник
comment
Нет примеров, кроме как в обсуждении сразу ниже?   -  person Tom Hawtin - tackline    schedule 13.12.2010
comment
Это правда, они приводят пример, но из него я не мог понять, что такое конверсии захвата. Вы поняли пример? Если да, то не могли бы вы объяснить мне, что они собой представляют. Спасибо.   -  person John Assymptoth    schedule 13.12.2010


Ответы (2)


Преобразование захвата было разработано, чтобы сделать подстановочные знаки (в обобщениях) ? полезными.

Предположим, у нас есть следующий класс:

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), в нем четко сказано:

Преобразование захвата — это то, что позволяет компилятору создать имя типа-заполнителя для захваченного подстановочного знака, чтобы вывод типа мог сделать вывод, что это именно тот тип.

Надеюсь, это поможет вам.

person Buhake Sindi    schedule 13.12.2010
comment
Еще один пример: stackoverflow.com/questions/4449611/ - person John Assymptoth; 18.12.2010
comment
@John, если вы прочитаете статью (ссылка, приведенная выше из IBMWorks), вы увидите, что приведенный вами пример взят оттуда. - person Buhake Sindi; 18.12.2010

Параметризованный тип, включающий аргументы подстановочного знака, на самом деле является типом объединения. Например

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.

person irreputable    schedule 05.09.2011