В чем разница между '?', 'E', и ' T ' для Java дженериков?
я сталкиваюсь с Java-кодом следующим образом:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
в чем разница между всеми тремя из вышеперечисленных и что они называют этим типом объявления класса или интерфейса в Java?
5 ответов:
хорошо, что нет никакой разницы между первыми двумя - Они просто используют разные имена для параметр типа (
EилиT).третий не является действительным объявлением -
?используется подстановочный знак который используется при определении типа аргумент, например,List<?> foo = ...означает, чтоfooотносится к списку какого-то типа, но мы не знаем, что.все это дженериков, который является довольно огромной тема. Вы можете узнать об этом через следующие ресурсы, хотя есть более доступные, конечно:
- Java учебник по дженерикам
- руководство по языку дженериков
- дженерики на языке программирования Java
- Анжелика Лангер Java Generics FAQ (массивный и всеобъемлющий; больше для справки, хотя)
это больше условности, чем что-либо еще.
Tдолжен быть типаEдолжен быть элемент (List<E>: список элементов)K- это ключ (вMap<K,V>)Vзначение (как возвращаемое значение или отображаемое значение)они полностью взаимозаменяемы (несмотря на конфликты в одной и той же декларации).
предыдущие ответы объясняют параметры типа (T, E и т. д.), но не объясняйте подстановочный знак,"?- или различия между ними, так что я обращусь к этому.
во-первых, просто чтобы быть ясным: подстановочные знаки и параметры типа не совпадают. Где параметры Тип определения вроде переменных (например, T), которая представляет собой тип рамки, шаблон не шаблон просто определяет набор допустимых типов, которые можно использовать для универсального типа. Без каких-либо ограничений (
extendsилиsuper), подстановочный знак означает "использовать любой тип здесь".подстановочный знак всегда находится между угловыми скобками, и он имеет значение только в контексте универсального типа:
public void foo(List<?> listOfAnyType) {...} // pass a List of any typeникогда
public <?> ? bar(? someType) {...} // error. Must use type params hereили
public class MyGeneric ? { // error public ? getFoo() { ... } // error ... }это становится более запутанным, где они перекрываются. Например:
List<T> fooList; // A list which will be of type T, when T is chosen. // Requires T was defined above in this scope List<?> barList; // A list of some type, decided elsewhere. You can do // this anywhere, no T required.есть много перекрытий в том, что возможно с определениями методов. Ниже приведены, функционально, идентично:
public <T> void foo(List<T> listOfT) {...} public void bar(List<?> listOfSomething) {...}Итак, если есть перекрытие, зачем использовать один или другой? Иногда, это честно просто стиль: Некоторые люди говорят, что если вы не нужно тип param, вы должны использовать подстановочный знак только для того, чтобы сделать код проще/более читаемым. Одно из основных различий я объяснил выше: параметры Тип определить тип переменной (например, Т), которые вы можете использовать в другом месте в области, по шаблону не. В противном случае, есть две большие разницы между параметрами тип и подстановочный знак:
параметры типа могут иметь несколько ограничивающих классов; подстановочный знак не может:
public class Foo <T extends Comparable<T> & Cloneable> {...}подстановочный знак может иметь нижние границы; тип params не может:
public void bar(List<? super Integer> list) {...}в выше
List<? super Integer>определяетIntegerкак нижняя граница подстановочного знака, что означает, что тип списка должен быть целым числом или супертипом целого числа. Ограничение общего типа выходит за рамки того, что я хочу подробно рассмотреть. Короче говоря, это позволяет определить типы a универсальный тип может быть. Это позволяет лечить дженерики полиморфно. Например, с:public void foo(List<? extends Number> numbers) {...}вы можете пройти
List<Integer>,List<Float>,List<Byte>и т. д. ибоnumbers. Без ограничения типа это не будет работать - вот как дженерики.наконец, вот определение метода, которое использует подстановочный знак, чтобы сделать что-то, что я не думаю, что вы можете сделать любым другим способом:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) { numberSuper.add(elem); }
numberSuperможет быть список чисел или любой супертип числа (например,List<Object>), иelemдолжно быть число или любой подтип. Со всеми ограничениями компилятор может быть уверен, что.add()типобезопасна.
переменная типа
может быть любым указанным вами непримитивным типом: любым типом класса, любым типом интерфейса, любым типом массива или даже другой переменной типа. наиболее часто используемые имена параметров типа:
- E-Element (широко используется в рамках коллекций Java)
- K-Key
- N - Количество
- T-Type
- V - Значение
в Java 7 это разрешено создавать такой:
Foo<String, Integer> foo = new Foo<>(); // Java 7 Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
компилятор сделает захват для каждого подстановочного знака (например, вопросительный знак в списке), когда он делает функции как:
foo(List<?> list) { list.put(list.get()) // ERROR: capture and Object are not identical type. }однако общий тип, такой как V, будет в порядке и сделает его универсальный метод:
<V>void foo(List<V> list) { list.put(list.get()) }
Comments