Границы параметров универсального типа и конкретный класс

Со страницы 49 OCP Java SE 6 Programmer Practice Exams, вопрос 11. Нам дан такой код:

class A { }
class B extends A { }
class C extends B { }
public class Carpet<V extends B> {
    public <X extends V> Carpet<? extends V> method(Carpet<? super X> e) {
        // insert code here
    }
}

И нас спрашивают: Что, независимо вставленное в отмеченную строку, будет компилироваться?

Итак, я тестирую первый ответ - return new Carpet<X>(). Оно работает. Хорошо, мои рассуждения выглядят так: если у меня есть возвращаемый тип Carpet<? extends V>, он должен возвращать что-то, что расширяет V. Просто раньше написано, что X extends V, так что отлично, X работает.

Следующий ответ - return new Carpet<V>(). Ну, это еще проще, потому что V уже "расширяет" V, так что я могу это вернуть. Работает, хорошо, хорошо.

Далее - return new Carpet<A>(). Это не работает, компилятор кричит:

Type parameter 'A' is not within its bound; should extend 'B'

Но я согласен. Это написано рядом с объявлением класса, V extends B. Итак, я понимаю, что могу вернуть что-то, что расширяет V, что расширяет B. Точно не A.

Итак, я вижу следующий ответ - return new Carpet<B>(). Уф, легко, компилятор только что сказал: «должен расширять B, поэтому B (как перед V для «чего-то, что расширяет V») должно быть просто идеальным. Но! Это не компиляция:

Error:(6, 16) java: incompatible types
  required: Carpet<? extends V>
  found:    Carpet<B>

Я не понимаю, почему. Объяснение, данное в книге:

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

Но, честно говоря, я до сих пор не понимаю. Я не уверен, что означает «точная область» в этом контексте, но компилятор сказал, что параметр возвращаемого типа должен расширять B. Итак, он может понять (как и я), что «что-то, что расширяет V», является «чем-то, что расширяет B». Итак, почему B не работает?

Не могли бы вы дать лучшее объяснение?


person Adam Stelmaszczyk    schedule 09.11.2014    source источник
comment
Ты вызвал у меня головную боль   -  person Dici    schedule 10.11.2014
comment
@Dici Ха, извини, это не было моим намерением. Хорошо, что не это.   -  person Adam Stelmaszczyk    schedule 10.11.2014


Ответы (1)


V — это общий тип ковра. При создании экземпляра ковра пользователь класса выбирает конкретный тип V. Это может быть B, C или любой другой класс, прямо или косвенно расширяющий B:

Carpet<B> carpetOfB = new Carpet<B>();

or

Carpet<C> carpetOfC = new Carpet<C>();

or

Carpet<Plane> carpetOfPlane = new Carpet<Plane>();

Что бы пользователь ни выбрал в качестве конкретного типа для V (скажем, он выбрал Plane, который расширяет C), метод должен возвращать Carpet типа, который расширяет V. Таким образом, в этом случае этот тип должен быть Plane или подтипом Plane. . И ясно, что B не подходит, поскольку не расширяет Plane.

person JB Nizet    schedule 09.11.2014
comment
+1 Кажется логичным, когда вы это говорите! Однако есть опечатка (Pnale вместо Plane). Я позволю тебе исправить это. - person Dici; 10.11.2014
comment
Спасибо. Исправлена ​​опечатка. - person JB Nizet; 10.11.2014
comment
+1 Пришлось перечитать это несколько раз, но в итоге дошло, большое спасибо! :) Итак, возвращаемый тип должен быть гибким (должен быть типа, который дал пользователь класса), а не жестко запрограммированным... теперь это имеет смысл :) - person Adam Stelmaszczyk; 10.11.2014