Я новый ученик java. Недавно я читал Generic-программирование и сам смутился с этим...
A<T extends B> and A<? extends B>
Я новый ученик java. Недавно я читал Generic-программирование и сам смутился с этим...
A<T extends B> and A<? extends B>
Прежде всего, это совершенно разные конструкции, используемые в разных контекстах.
A<T extends B>
является частью объявления типового типа, такого как
public class A<T extends B> { ... }
Он объявляет общий тип A
с параметром типа T
и вводит оценку на T
, так что T
должен быть подтипом B
.
A<? extends B>
является параметризованным типом с подстановочным знаком, его можно использовать в объявлениях переменных и методов и т.д. как обычный тип:
A<? extends B> a = ...;
public void foo(A<? extends B> a) { ... }
Объявление переменной, например A<? extends B> a
, означает, что тип A
есть A
, параметризованный некоторым подтипом B
.
Например, учитывая это объявление
List<? extends Number> l;
вы можете:
Назначьте List
некоторого подтипа от Number
до l
:
l = new ArrayList<Integer>();
Получить из этого списка объект типа Number
:
Number n = l.get(0);
Однако вы не можете поместить что-либо в список l
, так как вы не знаете фактический тип параметра в списке:
Double d = ...;
l.add(d); // Won't compile
?
- это то, что называется wildcard. Это означает все, что расширяет B.
Когда вы пишете List<T extends String>
, ваш List
может содержать только элементы типа T
.
Когда вы пишете List<? extends String>
, ваш List
может содержать любой элемент, который extends String
Надеюсь, я понятен, так как эти понятия сложны.
(длинный комментарий, а не ответ)
Совершенно разные вещи. Сходство в синтаксисе - серьезная ошибка, допущенная Java. И эта ошибка приводит к большей ошибке - многие люди пытаются понять подстановочный знак как параметр типа (например, подстановочный знак)
Не было такой вещи, как A<? extends B>
, пока Java не придумала ее. До тех пор синтаксис, используемый исследователями, был A<B+>
. Java подумала, что это слишком загадочно для наших бедных программистов, поэтому он придумал синтаксис A<? extends B>
, который, кажется, лучше читается в первом раунде.
Хорошо, это чертовски многословно и уродливо. Если API имеет несколько подстановочных знаков, это похоже на рвоту символов.
Но хуже то, что это путаница. Он выглядит как параметр типа. И Java сделала это специально! Java не верила, что ее программисты могут когда-либо понимать ковариантные типы, поэтому синтаксически это делало конвариантные типы похожими на параметризованные типы, направляя программистов на ошибочный способ понимания, который, по общему признанию, может быть полезен в случаях, но в конечном итоге делает людей не знающими.
Дженерики - сложный предмет в целом, и особенно в Java. Но в основном разница заключается в следующем:
Существует определенный тип T, он ограничен только B или подклассом B, но это определенный тип знания. Любая простая старая общая декларация похожа на высказывание: <T extends Object>
Говоря, что T extends B, мы говорим, что T более ограничен, чем любой объект, он должен быть конкретно типом B.
Это означает, что общий тип неизвестен точно, но то, что мы можем сказать об этом, состоит в том, что оно расширяет B. Это может быть B, это может быть подкласс B. С подстановочным знаком и расширением слова это означает что вы можете получить B из объекта, но вы не можете помещать что-либо в объект безопасным типом. (? super B означает обратное - вы можете поместить что-то в параметр метода, который является B или суперклассом B, но вы не можете быть уверены, каким будет возвращаемое значение метода.)