Я сомневаюсь, что есть какая-то разница. Я думаю, это просто имя для этого параметра типа. И последний ли вообще действителен?
CodesInChaos
Ответы:
231
Ну, нет никакой разницы между первыми двумя - они просто используют разные имена для параметра типа ( Eили T).
Третье не является допустимым объявлением - ?используется в качестве подстановочного знака, который используется при предоставлении аргумента типа , например, List<?> foo = ...означает, что fooотносится к списку некоторого типа, но мы не знаем, что.
Все это дженерики , и это довольно большая тема. Вы можете узнать об этом через следующие ресурсы, хотя, конечно, есть и другие:
Похоже, ссылка на PDF не работает. Я обнаружил , что , как представляется, скопировать здесь , но я не могу быть 100% уверен , так как я не знаю , что оригинал выглядел.
Джон
2
@Джон: Да, это тот. Будет ли редактировать ссылку, будь то один или Oracle ...
Джон Скит
Есть ли что-нибудь кроме T, E и? используется в дженериках? Если да, то, что они и что они значат?
sofs1
1
@ sofs1: В этом нет ничего особенного, Tи Eони просто идентификаторы. Вы могли бы написать, KeyValuePair<K, V>например. ?имеет особое значение, хотя.
Джон Скит
215
Это больше обычного, чем все остальное.
T предназначен для типа
Eдолжен быть элементом ( List<E>список элементов)
KКлюч (в Map<K,V>)
V Значение (в качестве возвращаемого значения или сопоставленного значения)
Они полностью взаимозаменяемы (несмотря на конфликты в одной и той же декларации).
Буква между <> это просто имя. То, что вы описываете в своем ответе, является просто условностями. Это даже не должна быть одна заглавная буква; Вы можете использовать любое имя, которое вам нравится, так же, как вы можете давать классы, переменные и т. д. любое имя, которое вам нравится.
Предыдущие ответы объясняют параметры типа (T, E и т. Д.), Но не объясняют подстановочный знак «?» Или различия между ними, поэтому я рассмотрю это.
Во-первых, просто для ясности: подстановочный знак и параметры типа не совпадают. Там, где параметры типа определяют вид переменной (например, T), которая представляет тип для области, подстановочный знак не делает: подстановочный знак просто определяет набор допустимых типов, которые вы можете использовать для универсального типа. Без каких-либо ограничений ( extendsили super) подстановочный знак означает «используйте любой тип здесь».
Подстановочные знаки всегда заключаются в угловые скобки, и они имеют значение только в контексте универсального типа:
publicvoid foo(List<?> listOfAnyType){...}// pass a List of any type
никогда
public<?>? bar(? someType){...}// error. Must use type params here
Это становится более запутанным, когда они перекрываются. Например:
List<T> fooList;// A list which will be of type T, when T is chosen.// Requires T was defined above in this scopeList<?> barList;// A list of some type, decided elsewhere. You can do// this anywhere, no T required.
Существует много совпадений в том, что возможно с определениями методов. Следующее, функционально, идентично:
Итак, если есть совпадение, зачем использовать один или другой? Иногда, честно говоря, это просто стиль: некоторые люди говорят, что если вам не нужен параметр типа, вы должны использовать подстановочный знак только для того, чтобы сделать код проще / более читабельным. Одно основное отличие, которое я объяснил выше: параметры типа определяют переменную типа (например, T), которую вы можете использовать в другом месте области действия; подстановочный знак не. В противном случае между параметрами типа и подстановочным знаком есть две большие разницы:
Тип params может иметь несколько ограничивающих классов; подстановочный знак не может:
Подстановочный знак может иметь нижние границы; Тип params не может:
publicvoid bar(List<?superInteger> list){...}
В приведенном выше List<? super Integer>описании определяется Integerкак нижняя граница подстановочного знака, означающего, что тип списка должен быть Integer или супер-тип Integer. Общее ограничение типов выходит за рамки того, что я хочу подробно описать. Короче говоря, это позволяет вам определить, какие типы могут быть универсальными. Это позволяет полиморфно относиться к генерикам. Например, с:
publicvoid foo(List<?extendsNumber> numbers){...}
Вы можете передать List<Integer>, List<Float>, List<Byte>и т.д. для numbers. Без ограничения типов это не сработает - вот каковы дженерики.
Наконец, вот определение метода, которое использует подстановочный знак, чтобы сделать что-то, что я не думаю, что вы можете сделать любым другим способом:
numberSuperможет быть Списком Чисел или любым супертипом Числа (например, List<Object>), и elemдолжен быть Числом или любым подтипом. При всех ограничениях компилятор может быть уверен, что .add()он безопасен для типов.
"public void foo (List <? extends Number> numbers) {...}" должен "extends" быть "super"?
1a1a11a
1
Нет. Цель этого примера - показать сигнатуру, которая полиморфно поддерживает список номеров и подтипов номеров. Для этого вы используете «extends». То есть, «передайте мне список номеров или что-нибудь, что расширяет номер» (List <Integer>, List <Float>, что угодно). Подобный метод может затем выполнить итерацию по списку и выполнить для каждого элемента «e», например, e.floatValue (). Не имеет значения, какой подтип (расширение) числа вы передаете - вы всегда сможете ".floatValue ()", потому что .floatValue () является методом Number.
Соколиный Глаз Паркер
В вашем последнем примере «List <? Super Number>» может быть просто «List <Number>», поскольку метод не допускает ничего более общего.
Джессара
@jessarah Нету. Возможно, мой пример неясен, но я упоминаю в примере, что adder () может принимать List <Object> (Object является суперклассом Number). Если вы хотите, чтобы он мог это сделать, он должен иметь подпись «Список <? Super Number>». В этом и заключается смысл "супер" здесь.
Соколиный Глаз Паркер
2
Этот ответ очень хорошо объясняет различия между подстановочными знаками и параметрами типов, с этим ответом должен быть один специальный вопрос. В последнее время я углубляюсь в дженерики, и этот ответ очень помог мне собрать все вместе, вкратце, много точной информации, спасибо!
Testo
27
Переменная типа <T> может быть любым указанным вами не примитивным типом: любым типом класса, любым типом интерфейса, любым типом массива или даже переменной другого типа.
Наиболее часто используемые имена параметров типа:
Электронный элемент (широко используется в Java Collections Framework)
Ответы:
Ну, нет никакой разницы между первыми двумя - они просто используют разные имена для параметра типа (
E
илиT
).Третье не является допустимым объявлением -
?
используется в качестве подстановочного знака, который используется при предоставлении аргумента типа , например,List<?> foo = ...
означает, чтоfoo
относится к списку некоторого типа, но мы не знаем, что.Все это дженерики , и это довольно большая тема. Вы можете узнать об этом через следующие ресурсы, хотя, конечно, есть и другие:
источник
T
иE
они просто идентификаторы. Вы могли бы написать,KeyValuePair<K, V>
например.?
имеет особое значение, хотя.Это больше обычного, чем все остальное.
T
предназначен для типаE
должен быть элементом (List<E>
список элементов)K
Ключ (вMap<K,V>
)V
Значение (в качестве возвращаемого значения или сопоставленного значения)Они полностью взаимозаменяемы (несмотря на конфликты в одной и той же декларации).
источник
Предыдущие ответы объясняют параметры типа (T, E и т. Д.), Но не объясняют подстановочный знак «?» Или различия между ними, поэтому я рассмотрю это.
Во-первых, просто для ясности: подстановочный знак и параметры типа не совпадают. Там, где параметры типа определяют вид переменной (например, T), которая представляет тип для области, подстановочный знак не делает: подстановочный знак просто определяет набор допустимых типов, которые вы можете использовать для универсального типа. Без каких-либо ограничений (
extends
илиsuper
) подстановочный знак означает «используйте любой тип здесь».Подстановочные знаки всегда заключаются в угловые скобки, и они имеют значение только в контексте универсального типа:
никогда
или
Это становится более запутанным, когда они перекрываются. Например:
Существует много совпадений в том, что возможно с определениями методов. Следующее, функционально, идентично:
Итак, если есть совпадение, зачем использовать один или другой? Иногда, честно говоря, это просто стиль: некоторые люди говорят, что если вам не нужен параметр типа, вы должны использовать подстановочный знак только для того, чтобы сделать код проще / более читабельным. Одно основное отличие, которое я объяснил выше: параметры типа определяют переменную типа (например, T), которую вы можете использовать в другом месте области действия; подстановочный знак не. В противном случае между параметрами типа и подстановочным знаком есть две большие разницы:
Тип params может иметь несколько ограничивающих классов; подстановочный знак не может:
Подстановочный знак может иметь нижние границы; Тип params не может:
В приведенном выше
List<? super Integer>
описании определяетсяInteger
как нижняя граница подстановочного знака, означающего, что тип списка должен быть Integer или супер-тип Integer. Общее ограничение типов выходит за рамки того, что я хочу подробно описать. Короче говоря, это позволяет вам определить, какие типы могут быть универсальными. Это позволяет полиморфно относиться к генерикам. Например, с:Вы можете передать
List<Integer>
,List<Float>
,List<Byte>
и т.д. дляnumbers
. Без ограничения типов это не сработает - вот каковы дженерики.Наконец, вот определение метода, которое использует подстановочный знак, чтобы сделать что-то, что я не думаю, что вы можете сделать любым другим способом:
numberSuper
может быть Списком Чисел или любым супертипом Числа (например,List<Object>
), иelem
должен быть Числом или любым подтипом. При всех ограничениях компилятор может быть уверен, что.add()
он безопасен для типов.источник
Переменная типа <T> может быть любым указанным вами не примитивным типом: любым типом класса, любым типом интерфейса, любым типом массива или даже переменной другого типа.
Наиболее часто используемые имена параметров типа:
В Java 7 разрешено создавать такие экземпляры:
источник
Наиболее часто используемые имена параметров типа:
Вы увидите, что эти имена используются в API Java SE
источник
Компилятор сделает захват для каждого подстановочного знака (например, вопросительного знака в Списке), когда он составляет такую функцию:
Однако универсальный тип, такой как V, будет в порядке и сделает его универсальным методом :
источник