|
Обобщенные типы позволяют совместить выгоды от переиспользования кода для разных типов данных и строгую проверку типов компилятором.
Получая элемент из коллекции объектов (Collection), его
необходимо явным образом привести к типу элемента, который сохранен в коллекции.
Кроме того, что это неудобно, это еще и потенциальная проблема. Компилятор не может проверить соответствует ли фактический тип элемента данных типу на который вы расчитываете в операции приведения.
Поэтому во время работы программы может возникнуть неожиданное исключение.
Обобщенные типы позволяют сообщить тип объектов коллекции компилятору
языка Java. Поскольку компилятор "знает" тип элементов
коллекции, он может проверить, что Вы помещаете туда значение
соответствующего типа и может осуществить приведение типа при попытке
получить значение из коллекции.
Вот - простой пример, взятый из существующей обучающей программы Collection:
// Удаление 4-х значных слов из c. Элементы должны быть типа strings
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); )
if (((String) i.next()).length() == 4)
i.remove();
}
А вот тот же самый пример с использованием обобщенных типов:
// Удаление 4-х значных слов из c
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}
Когда Вы видите код <Type>, читайте это как "of Type"; описание
выше читается как "коллекция строк". Код, используя обобщенные типы,
становится более ясным и безопасным. Мы устранили опасное приведение и
множество дополнительных круглых скобок. Что еще более важно, мы
переместили часть спецификации метода из комментария в его сигнатуру,
так что компилятор может проверить при компиляции, что ограничения типа
не нарушаются во время выполнения программы. Поскольку программа
компилируется без предупреждений, можно с уверенностью заявить, что во
время выполнения программы не появится ClassCastException.
Результирующим влиянием использования обобщенных типов, особенно в
больших программах, является улучшение удобочитаемости и надежности.
Перефразируя Спецификацию по обобщенным типам (Gilad Bracha),
можно сказать, что если переменная c объявлена как
Collection<String>, это говорит о том, что переменная c,
сохраняет свой тип везде и всякий раз, когда используется. Кроме того,
компилятор гарантирует это (предполагается, что программа компилируется
без предупреждений). С другой стороны, приведение, говорит о том, что
программист считает, что переменная содержит значение некоторого типа,
и виртуальная машина проверяет, прав ли программист только во время
выполнения программы.
Помимо первичного использования обобщенных типов в качестве коллекций,
есть много других применений. "Holder classes", типа WeakReference и
ThreadLocal, весь был обобщен, то есть был подогнан для использования
обобщенных типов. Еще удивительнее, что и класс Class был обобщен.
Имена классов теперь функционирует как тип идентификаторы, обеспечивая
нас информацией о типе как во время выполнения, так и во время
компиляции. Что позволяет использовать статические фабрики,
иллюстрируемых методом getAnnotation в новом интерфейсе
AnnotatedElement:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
Это - обобщенный метод. Он выводит значение параметра типа T от
его аргумента и возвращает соответствующее событие T, как показано
ниже:
Author a = Othello.class.getAnnotation(Author.class);
До обобщенных типов Вы должны были бы привести результат к Author.
Также Вы не смогли бы осуществить проверку на этапе компиляции, что
фактический параметр представляет подкласс класса Annotation.
Обобщенные типы реализуются стиранием типа: обобщенная информация типа
присутствует только во время компиляции, после которой она стирается
компилятором. Главное преимущество этого подхода состоит в том, что
обеспечивается общая функциональная совместимость между обобщенным
кодом и традиционным кодом с использованием несформированного
типа данных (raw types). Основными недостатками
являются то, что информация о параметре является недоступной во время
выполнения программы, и что автоматически генерируемое приведение может
выходить из строя при взаимодействии с традиционным кодом, имеющим
плохое поведение. Однако, существует способ достичь гарантированной
безопасности типов во время исполнения для коллекции обобщенных типов,
даже когда происходит взаимодействие
с традиционным кодом, имеющим плохое поведение.
Класс java.util.Collections снабжен классами оболочки, которые
обеспечивают гарантированную безопасность типов во время выполнения
программы. Они подобны по структуре синхронизированным и неподдающимся
изменению оболочкам. Эти "оболочки проверяемых
коллекций" являются очень полезными для отладки. Предположим, что Вы
имеете набор строк, скажем, s в который некоторый традиционный код по случайности
вставляет целое число. Без оболочки Вы не узнаете о проблеме пока Вы
не прочитаете данный элемент из набора, и автоматическое
приведение к строковому типу не потерпит неудачу. В этот момент уже
слишком поздно определять источник проблемы. Однако, если заменить
описание:
Set<String> s = new HashSet<String>();
на такое описание:
Set<String> s = Collections.checkedSet(new HashSet<String>(), String.class);
коллекция сгенерирует ClassCastException в точке, где традиционный код пытается вставить целое число.
В результате запись истории стека позволит Вам диагностировать и
восстанавить проблему. Вы должны использовать обобщенные типы везде,
где это возможно. Считается оправднанным потратить некоторые дополнительные усилия на обобщение кода
ради увеличения ясности и безопасности типов.
Достаточно просто использовать обобщенную библиотеку классов, но требуются
некоторые специальные знания для написания обобщенной библиотеки, или для обобщения существующей библиотеки.
Есть одно
предостережение: Вы не можете использовать обобщенные типы (как и некоторые другие новые возможности JDK 5.0), если Вы собираетесь использовать
компилируемый код на виртуальной машине, более ранней, чем JDK 5.0.
Кстати, на первый взгляд может показаться, что обобщенные типы в Java подобны шаблонам (templates) в C++. Сходство конечно есть, но оно поверхностно. Обобщенные типы не
вызывают генерации нового класса для каждого конкретного типа параметра обобщенного класса. Обобщенные типы также не поддерживают метапрограммирование на основе шаблонов.
Чтобы узнать больше об обобщенных типах рекомендуем ознакомиться с Generics Tutorial.
Оригинал Перевод Саноцкая Н., Дмитриев А.
|