Игры С Целью - который разрабатывают инструменты Коллективного разума как Luis von Ahn и его команда, возможно, были мечтой до 1980, но не было широко развернутой сети с миллионами доступных людей и потребность (например, reCAPTCHA), чтобы на самом деле заставить ее произойти.
Что вы делаете в пример «без универсальных» - это приведение, которое дает понять, что вы делаете что-то небезопасное для типов. Эквивалент для дженериков будет:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
Который ведет себя одинаково (компилируется, но не работает во время выполнения). Причина запрета поведения, о котором вы спрашиваете, заключается в том, что оно допускает неявные небезопасные по типу действия, которые гораздо труднее заметить в коде. Например:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
Это выглядит прекрасно и безопасно по типам, пока вы не назовете его так:
List<Double> list_d;
String s;
Foo(list_d, s);
Что также выглядит типобезопасным, потому что вы, как вызывающий, не обязательно знаете, что Foo собирается делать со своими параметрами.
] Итак, в этом случае у вас есть два, казалось бы, типобезопасных бита кода, которые вместе оказываются типо-небезопасными. Это плохо, потому что это скрыто, и поэтому его трудно избежать и труднее отладить.
Подумайте, если бы это было ...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
Вы могли бы добавить в список что-нибудь, относящееся к родительскому типу, которое может отличаться от того, как оно было объявлено ранее, что, как показывает пример выше, вызывает всевозможные проблемы. Таким образом, это запрещено.
По сути, здесь есть два измерения абстракции: абстракция списка и абстракция его содержимого. Совершенно нормально варьировать абстракцию списка - например, сказать, что это LinkedList или ArrayList, - но нельзя еще больше ограничивать содержимое, например: This (список, содержащий объекты) является (связанным списком, который содержит только числа). Поскольку любая ссылка, которая знает его как (список, содержащий объекты), в соответствии с контрактом своего типа понимает что он может содержать любой объект.
Это сильно отличается от того, что вы делали в коде, не являющемся универсальным, где вы сказали: относитесь к этой String, как если бы она была Double. Вместо этого вы пытаетесь сказать: относитесь к этому (списку, который содержит только числа) как к (списку, который содержит что угодно). И это не так, и компилятор может обнаружить это, поэтому он не позволит вам избежать наказания за это.
Они не являются подтипами друг друга из-за того, как работают дженерики. Вы хотите объявить свою функцию следующим образом:
public void wahey(List<?> list) {}
Затем она примет Список всего, что расширяет Object. Вы также можете:
public void wahey(List<? extends Number> list) {}
Это позволит вам получить списки чего-то, что является подклассом Number.
Я бы порекомендовал вам взять копию "Java Generics and Collections" Мориса Нафталина и Филипа Вадлера.
«То, что мы здесь делаем, по сути то же самое, только это пройдет проверки во время компиляции и терпят неудачу только при время выполнения. Версия со списками не будет compile. "
То, что вы наблюдаете, имеет смысл, если учесть, что основная цель дженериков Java - добиться сбоя несовместимости типов во время компиляции, а не во время выполнения.
Generics позволяет сообщить тип коллекции компилятору, чтобы он мог быть проверил. Как только компилятор узнает тип элемента коллекции, компилятор может проверить, что вы использовали сбор последовательно и может вставьте правильные приведения значений удаляются из коллекции.