Ответ 1
Как точка pedntry, существует разница, если класс /interface/constructor/method объявляет привязку (кроме extends Object
).
interface Donkey<T extends Thing> { }
...
Donkey<? extends Object> foo; // FAIL
Есть ли разница между неограниченным подстановочным знаком, например. <?>
и ограниченный подстановочный знак, граница которого Object
, например. <? extends Object>
?
Я помню, как где-то читал, что в ранних черновиках дженериков существует разница, но больше не может найти этот источник.
Как точка pedntry, существует разница, если класс /interface/constructor/method объявляет привязку (кроме extends Object
).
interface Donkey<T extends Thing> { }
...
Donkey<? extends Object> foo; // FAIL
С практической точки зрения для большинства людей <? extends Object>
совпадает с <?>
, как это все предлагали здесь.
Однако они отличаются двумя очень незначительными и тонкими точками:
JVMS (спецификация виртуальной машины Java) имеет специальную спецификацию для неограниченных подстановочных знаков, так как ClassFileFormat-Java5
указывает, что неограниченный шаблон будет закодирован как *
, в то время как кодирует ограниченный объектами подстановочный знак как +Ljava/lang/Object;
. Такое изменение протекает через любую библиотеку, которая анализирует байт-код. Писателям-компиляторам тоже придется заниматься этой проблемой. Из изменений в "Формат файла класса"
С точки зрения reifiablity, они разные. JLS 4.6 и 4.7 кодифицируйте List<?>
как тип повторного ввода, но List<? extends Object>
как стираемый тип. Любой администратор библиотеки, добавляющий .isReifiable()
(например, mjc lib), должен учитывать это, придерживаясь терминологии JLS. Из JLS 4.6 и 4.7.
Из экспериментов кажется, что, например, List<?>
и List<? extends Object>
совместимы с назначением в обоих направлениях, а метод, который имеет подпись с использованием одного из них, может быть переопределен сигнатурой, использующей другую. например.
import java.util.List;
class WildcardTest<T> {
public void foo(List<? extends T> bar) {}
}
class WildcardTest2 extends WildcardTest<Object> {
@Override
public void foo(List<?> bar) {super.foo(bar);}
}
Это сложно...
Для любой переменной типа T
спецификация говорит http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4
Каждая переменная типа... имеет границу. Если для переменной типа не объявлена никакая граница, предполагается, что предполагается объект.
Можно было бы подумать, что это верно и для подстановочных знаков, а ?
должно быть просто сокращением для ? extends Object
.
Однако, просматривая спецификацию, нет никаких доказательств того, что шаблон должен иметь верхнюю границу (или нижнюю границу). "Неограниченный" ?
обрабатывается последовательно от ограниченных подстановочных знаков.
Мы могли бы вывести из правил подтипирования, что List<?>
и List<? extends Object>
являются подтипами друг друга, то есть они в основном одного и того же типа.
Но спецификация рассматривает их отдельно. Например http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.7 List<?>
является повторно идентифицируемым типом, но List<? extends Object>
нет, что означает
// ok
List<?>[] xx = {};
// fail
List<? extends Object>[] yy = {};
// ok
boolean b1 = (y instanceof List<?>);
// fail
boolean b2 = (y instanceof List<? extends Object>);
Я не понимаю, почему. Кажется совершенно прекрасным сказать, что подстановочный знак должен иметь верхнюю границу и нижнюю границу, по умолчанию - Object
и null type
.
Все в java, за исключением примитивов, расширяет Object, поэтому нет, не было бы никакой разницы. Autoboxing позволяет использовать примитивы, поэтому можно сказать, что все в java - это объект.
<? extends Object>
ТОЧНО совпадает с <?>
. Извините, у меня нет ссылки, но... это так.:)
EDIT: конечно, я думал только с определенной точки зрения, когда я это сказал. Игнорируйте мой ответ (который был довольно правильно опущен) и посмотрите ответы с более высоким рейтингом для реальной истории.