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

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

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

Ответ 1

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

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

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() в "hindsight" возвращает ?, компилятор знает, что там 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, следовательно, он создает местозаполнитель, называемый capture.

Из здесь (теория и практика Java: дикая с дженериками, часть 1), в ней четко говорится:

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

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

Ответ 2

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

List<? extends Number> = Union{ List<S> | S <: Number }

В 2 случаях вместо использования 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