Разница между списком, списком <?>, Списком <T>, списком <E> и списком <объект>
В чем разница между List
, List<?>
, List<T>
, List<E>
и List<Object>
?
Теперь я не слепо задаю этот вопрос, поэтому, пожалуйста, не закрывайте эту тему. Позвольте мне сначала ввести базовый код:
private static List<String> names = new ArrayList<String>();
static {
names.add("Tom");
names.add("Peter");
names.add("Michael");
names.add("Johnson");
names.add("Vlissides");
}
public static void test(List<String> set){
System.out.println(set);
}
public static void main(String args[]){
test(names);
}
Я понимаю, что:
1. List
: это необработанный тип, поэтому не typesafe
. Это приведет к генерации ошибки во время выполнения, когда кастинг будет плохим. Мы хотим, чтобы ошибка времени компиляции была неудачной. Не рекомендуется использовать.
2. List<?>
: - неограниченный подстановочный знак. Но не уверен, для чего это? Поэтому, если я изменил метод test
на
public static void test(List<?> set){
System.out.println(set);
}
он все еще работает хорошо. Если вы можете объяснить использование этого, я был бы очень признателен.
EDIT: если я это сделаю:
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
но если я изменю test
на это:
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
3. List<T>
:
public static void test(List<T> set){ //T cannot be resolved
System.out.println(set);
}
Я думаю, я не понимаю этот синтаксис. Я видел что-то вроде этого, и он работает:
public <T> T[] toArray(T[] a){
return a;
}
Пожалуйста, объясните это мне, пожалуйста? Иногда я вижу <T>
, или <E>
, или <U>
, <T,E>
. Все они одинаковые или они представляют что-то другое?
4. List<Object>
public static void test(List<Object> set){
System.out.println(set);
}
Затем я получил ошибку The method test(List<Object>) is not application for the argument List<String>
для приведенного ниже кода. Я запутался. Я думал, что String
является подмножеством Object
?
public static void main(String args[]){
test(names);
}
EDIT: Если я попробую это
test((List<Object>)names);
то я получил Cannot cast from List<String> to List<Object>
Ответы
Ответ 1
1) Исправить
2) Вы можете думать о том, что это список "только для чтения", где вам не нужен тип элементов. Может быть, например. используется методом, который возвращает длину списка.
3) T, E и U одинаковы, но люди склонны использовать, например, T для типа, E для элемента, V для значения и K для ключа. Метод, который компилирует, говорит, что он принял массив определенного типа и возвращает массив того же типа.
4) Вы не можете смешивать апельсины и яблоки. Вы могли бы добавить объект в свой список строк, если бы вы могли передать список строк методу, который ожидает списки объектов. (И не все объекты являются строками)
Ответ 2
Для последней части:
Хотя String является подмножеством Object, но List <String> не унаследован от List <Object> .
Ответ 3
Обозначение List<?>
означает "список чего-то (но я не говорю, что)". Поскольку код в test
работает для любого типа объекта в списке, это работает как параметр формального метода.
Используя параметр типа (как в вашей точке 3), требуется, чтобы параметр типа был объявлен. Синтаксис Java для этого - поставить <T>
перед функцией. Это точно аналогично объявлению формальных имен параметров методу перед использованием имен в теле метода.
Относительно List<Object>
не принимающего List<String>
, это имеет смысл, потому что a String
не Object
; это подкласс Object
. Исправление состоит в объявлении public static void test(List<? extends Object> set) ...
. Но тогда extends Object
является избыточным, потому что каждый класс прямо или косвенно расширяет Object
.
Ответ 4
Причина, по которой вы не можете использовать List<String>
to List<Object>
, заключается в том, что она позволит вам нарушить ограничения List<String>
.
Подумайте о следующем сценарии: если у меня есть List<String>
, он должен содержать только объекты типа String
. (Который является классом final
)
Если я могу применить это к List<Object>
, то это позволяет мне добавить Object
в этот список, тем самым нарушив исходный контракт List<String>
.
Таким образом, в общем случае, если класс C
наследуется от класса P
, вы не можете сказать, что GenericType<C>
также наследуется от GenericType<P>
.
N.B. Я уже прокомментировал это в предыдущем ответе, но хотел расширить его.
Ответ 5
Я бы посоветовал читать головоломки Java. Он объясняет наследование, дженерики, абстракции и подстановочные знаки в декларациях достаточно хорошо.
http://www.javapuzzlers.com/
Ответ 6
В третьем пункте "T" не может быть разрешено, поскольку его не объявлено, обычно, когда вы объявляете общий класс, вы можете использовать "T" в качестве имени параметр связанного типа, многие онлайн-примеры, включая учебные пособия по оракулу используют "T" в качестве имя параметра типа, скажем, например, вы объявляете класс вроде:
public class FooHandler<T>
{
public void operateOnFoo(T foo) { /*some foo handling code here*/}
}
вы говорите, что метод FooHandler's
operateOnFoo
ожидает переменную типа T, которая объявляется в самом объявлении класса, имея в виду это, вы можете позже добавить другой метод, например
public void operateOnFoos(List<T> foos)
во всех случаях либо T, E, либо U есть все идентификаторы параметра типа, вы можете даже иметь более одного параметра типа, который использует синтаксис
public class MyClass<Atype,AnotherType> {}
в вашем четвертом ponint, хотя эффективно Sting является подтипом Object, в классах generics нет такого отношения, List<String>
не является подтипом List<Object>
, это два разных типа с точки зрения компилятора, это лучше всего объяснить в этой записи в блоге
Ответ 7
Проблема 2 в порядке, потому что "System.out.println(set);" означает "System.out.println(set.toString()); set является экземпляром List, поэтому complier вызовет List.toString();
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
Element ? will not promise Long and String, so complier will not accept Long and String Object
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object
Проблема 3: эти символы одинаковы, но вы можете дать им спецификацию differet. Например:
public <T extends Integer,E extends String> void p(T t, E e) {}
Задача 4: Сбор не допускает ковариацию параметров типа. Но массив разрешает ковариацию.
Ответ 8
Давайте поговорим о них в контексте истории Java;
Список означает, что он может включать любой объект. Список был в выпуске до Java 5.0; Java 5.0 представил список для обратной совместимости.
List list=new ArrayList();
list.add(anyObject);
-
List<?>
:
?
означает неизвестный объект, а не объект; подстановочный знак ?
предназначен для решения проблемы, созданной Generic Type; см. wildcards;
но это также вызывает еще одну проблему:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
-
List< T> List< E>
Обозначает общую декларацию в предпосылке ни одного типа T или E в вашем проекте Lib.
-
List< Object>
означает общую параметризацию.
Ответ 9
Вы правы: String является подмножеством объекта. Поскольку String является более "точным", чем Object, вы должны использовать его для использования в качестве аргумента для System.out.println().