Разница между списком, списком <?>, Списком <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;

  • List:

Список означает, что он может включать любой объект. Список был в выпуске до Java 5.0; Java 5.0 представил список для обратной совместимости.

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>:

? означает неизвестный объект, а не объект; подстановочный знак ? предназначен для решения проблемы, созданной Generic Type; см. wildcards; но это также вызывает еще одну проблему:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

Обозначает общую декларацию в предпосылке ни одного типа T или E в вашем проекте Lib.

  1. List< Object> означает общую параметризацию.

Ответ 9

Вы правы: String является подмножеством объекта. Поскольку String является более "точным", чем Object, вы должны использовать его для использования в качестве аргумента для System.out.println().