Generics
Содержание
Generics
Основным мотивом введения Generics в Java было неудобство работы с нетипизированными коллекциями. Использование нетипизированных коллекций приводит к большим трудозатратам, нежели типизированных, причем может порождать ошибки. Эти ошибки обнаруживаются только во время исполнения. Можно каждый раз, когда требуется коллекция, создавать ее типизированный вариант. Это избавляет от проблем, связанных с применением коллекций, но приводит к большому объему дублированию кода, создаваемого с помощью печально известной технологии copy-paste. Generic-и позволяют избежать подобных проблем за счет введения специальных параметров типов и обобщения реализации классов и методов. Они позволяют не создавать отдельную копию типизированной коллекции с измененным типом элемента, а создать единую обобщенную реализацию, в которой тип элемента заменен параметром типа, и впоследствии переложить работу по созданию специализированных коллекций на компилятор.
Свойства
- Строгая типизация
- Единая реализация
- Отсутствие информации о типе
Пример реализации Generic-класса
public interface List<E> extends Collection<E> { E get(int i); set(int i, E e); add(E e); Iterator<E> iterator(); … }
Теперь рассмотрим чем старая реализация кода отличается от новой :
List<E> ─ список элементов E
Раньше :
List list = new List(); list.add(new Integer(1)); Integer i = (Integer) list.get(0);
Теперь :
List<Integer> list = new List<Integer>(); list.add(new Integer(1)); Integer i = list.get(0);
Как видите, больше не нужно приводить Integer, так как метод get() возвращает ссылку на объект конкретного типа (в данном случае – Integer).
Несовместимость generic-типов
Generic-типы не совместимы по присваиванию
List<Integer> li = new ArrayList<Integer>(); List<Object> lo = li;
Иначе — ошибки
lo.add(“hello”); // ClassCastException Integer li = lo.get(0);
Проблемы реализации Generics
- Решение 1 - Wildcard
Проблема
void dump(Collection<Object> c) { for (Iterator<Object> i = c.iterator(); i.hasNext(); ) { Object o = i.next(); System.out.println(o); } }
List<Object> l; dump(l); List<Integer> l; dump(l); // Ошибка
Ошибка возникает, так как коллекция имеет свои ограничения в использовании. Для решения этой проблемы используется Wildcard ("?"). Он не имеет ограничения в использовании и в этом его плюсы. В этом примере List<Integer> не может использовать метод dump, так как он не является подтипом List<Object>.
Решение
void dump(Collection<?> c) { for (Iterator<?> i = c.iterator(); i.hasNext(); ) { Object o = i.next(); System.out.println(o); } }
- Решение 2 – Bounded Wildcard
Проблема
void draw(List<Shape> c) { for (Iterator<Shape> i = c.iterator(); i.hasNext(); ) { Shape s = i.next(); s.draw(); } }
List<Shape> l; draw(l); List<Circle> l; draw(l); // Ошибка
Это решение используют, если метод который нужно реализовать использовал бы определенный тип и его подтипов. Так называемое "Ограничение сверху". В Этом примере Circle является подтипом Shape.
Решение
void draw(List<? extends Shape> c) { for (Iterator<? extends Shape> i = c.iterator(); i.hasNext(); ) { Shape s = i.next(); s.draw(); } }
- Решение 3 – Generic-Метод
Проблема
void addAll(Object[] a, Collection<?> c) { for (int i = 0; i < a.length; i++) { c.add(a[i]); } }
addAll(new String[10], new ArrayList<String>()); addAll(new Object[10], new ArrayList<Object>()); addAll(new Object[10], new ArrayList<String>()); // Ошибка addAll(new String[10], new ArrayList<Object>()); // Ошибка
Решение
<T> void addAll(T[] a, Collection<T> c) { for (int i = 0; i < a.length; i++) { c.add(a[i]); } }
Но все равно после выполнение останется ошибка в третей строчке :
addAll(new Object[10], new ArrayList<String>()); // Ошибка