Что такое преобразование захвата в Java, и кто-нибудь может привести примеры? - PullRequest
15 голосов
/ 13 декабря 2010

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

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

Ответы [ 2 ]

20 голосов
/ 13 декабря 2010

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

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

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, поэтому он создает заполнитель с именем capture of .

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

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

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

1 голос
/ 06 сентября 2011

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

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

...