Отсутствие необходимости в типизации - одно из самых больших преимуществ универсальных шаблонов Java , так как он выполняет проверку типов во время компиляции. Это уменьшит вероятность ClassCastException
с, которые могут быть выброшены во время выполнения, и может привести к более устойчивому коду.
Но я подозреваю, что вы полностью осведомлены об этом.
Каждый раз, когда я смотрю на дженерики, это дает
у меня болит голова. Я нахожу лучшую часть
Java для простоты и минимальности
синтаксис и обобщения не просты и
добавить значительное количество новых
синтаксис.
Сначала я тоже не видел пользы от дженериков. Я начал изучать Java с использованием синтаксиса 1.4 (хотя Java 5 отсутствовал), и когда я столкнулся с дженериками, я почувствовал, что это было больше кода для написания, и я действительно не понял преимуществ.
Современные IDE упрощают написание кода с помощью дженериков.
Большинство современных, достойных IDE достаточно умны, чтобы помочь в написании кода с использованием обобщений, особенно при завершении кода.
Вот пример создания Map<String, Integer>
с HashMap
. Код, который мне нужно будет набрать:
Map<String, Integer> m = new HashMap<String, Integer>();
И действительно, это много, чтобы напечатать просто, чтобы сделать новый HashMap
. Однако на самом деле мне нужно было набрать это много раз, прежде чем Eclipse понял, что мне нужно:
Map<String, Integer> m = new Ha
Ctrl + Пробел
Правда, мне нужно было выбрать HashMap
из списка кандидатов, но в основном IDE знала, что добавить, включая общие типы. С правильными инструментами использование дженериков не так уж и плохо.
Кроме того, поскольку типы известны, при извлечении элементов из универсальной коллекции IDE будет действовать так, как если бы этот объект уже являлся объектом объявленного типа - нет необходимости в преобразовании для IDE, чтобы знать, что тип объекта.
Ключевое преимущество дженериков заключается в том, что они хорошо работают с новыми функциями Java 5. Вот пример добавления целых чисел в Set
и вычисления его общего числа:
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);
int total = 0;
for (int i : set) {
total += i;
}
В этом фрагменте кода представлены три новые функции Java 5:
Во-первых, дженерики и автобокс примитивов допускают следующие строки:
set.add(10);
set.add(42);
Целое число 10
автоматически помещается в Integer
со значением 10
. (И то же самое для 42
). Затем этот Integer
отбрасывается в Set
, который, как известно, содержит Integer
с. Попытка добавить String
приведет к ошибке компиляции.
Далее, цикл for-each принимает все три из них:
for (int i : set) {
total += i;
}
Во-первых, Set
, содержащий Integer
s, используются в цикле for-each. Каждый элемент объявляется как int
, и это разрешено, поскольку Integer
распаковывается обратно в примитив int
. И тот факт, что эта распаковка происходит, известен, потому что дженерики использовались, чтобы указать, что в Set
.
было проведено
Integer
s.
Обобщения могут быть связующим звеном, объединяющим новые функции, представленные в Java 5, и это делает кодирование проще и безопаснее. И в большинстве случаев интегрированные среды разработки достаточно умны, чтобы помочь вам с хорошими предложениями, поэтому, как правило, печатать их будет намного сложнее.
И, честно говоря, как видно из примера Set
, я чувствую, что использование функций Java 5 может сделать код более кратким и надежным.
Редактировать - пример без генериков
Ниже приведен пример вышеприведенного примера Set
без использования шаблонов. Это возможно, но не совсем приятно:
Set set = new HashSet();
set.add(10);
set.add(42);
int total = 0;
for (Object o : set) {
total += (Integer)o;
}
(Примечание. Приведенный выше код выдаст предупреждение о непроверенном преобразовании во время компиляции.)
При использовании неуниверсальных коллекций типами, которые вводятся в коллекцию, являются объекты типа Object
. Следовательно, в этом примере Object
- это то, что add
вводится в набор.
set.add(10);
set.add(42);
В приведенных выше строках автобокс находится в игре - примитивы int
value 10
и 42
автоматически помещаются в Integer
объекты, которые добавляются в Set
. Однако имейте в виду, что объекты Integer
обрабатываются как Object
s, так как нет информации о типе, которая помогла бы компилятору узнать, какой тип должен ожидать Set
.
for (Object o : set) {
Это та часть, которая имеет решающее значение. Причина, по которой цикл for-each работает, заключается в том, что Set
реализует интерфейс Iterable
, который возвращает Iterator
с информацией о типе, если она есть. (Iterator<T>
, то есть.)
Однако, поскольку нет информации о типе, Set
вернет Iterator
, который вернет значения в Set
как Object
с, и поэтому элемент, извлекаемый в for- каждая петля должна быть типа Object
.
Теперь, когда Object
извлечено из Set
, его необходимо привести к Integer
вручную, чтобы выполнить сложение:
total += (Integer)o;
Здесь выполняется типизация от Object
до Integer
. В этом случае мы знаем, что это всегда будет работать, но ручное приведение типов всегда заставляет меня чувствовать, что это хрупкий код, который может быть поврежден, если в другом месте будут сделаны незначительные изменения. (Я чувствую, что каждый тип передачи ClassCastException
ждет своего часа, но я отвлекся ...)
Integer
теперь распакован в int
и может выполнять добавление в переменную int
total
.
Я надеюсь, что смогу проиллюстрировать, что новые функции Java 5 можно использовать с неуниверсальным кодом, но он не так чист и прямолинеен, как при написании кода с обобщениями. И, на мой взгляд, чтобы в полной мере воспользоваться новыми возможностями Java 5, следует изучить универсальные шаблоны, если, по крайней мере, разрешить проверки во время компиляции, чтобы недопустимые типы типов генерировали исключения во время выполнения.