Generics — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Проблемы реализации Generics)
(Проблемы реализации Generics)
Строка 62: Строка 62:
 
   List<Integer> l; dump(l); // '''Ошибка'''
 
   List<Integer> l; dump(l); // '''Ошибка'''
  
Ошибка возникает, так как коллекция имеет свои ограничения в использовании. Для решения этой проблемы используется Wildcard ("?"). Он не имеет ограничения в использовании и в этом его плюсы.
+
Ошибка возникает, так как коллекция имеет свои ограничения в использовании. Для решения этой проблемы используется Wildcard ("?"). Он не имеет ограничения в использовании и в этом его плюсы. В этом примере List<Integer> не может использовать метод dump, так как он не является подтипом List<Object>. 
  
 
'''Решение'''  
 
'''Решение'''  
Строка 85: Строка 85:
 
   List<Circle> l; draw(l); // '''Ошибка'''
 
   List<Circle> l; draw(l); // '''Ошибка'''
  
 +
Это решение используют, если метод который нужно реализовать использовал бы определенный тип и его подтипов. Так называемое "Ограничение сверху". В Этом примере Circle является подтипом Shape.
 
'''Решение'''
 
'''Решение'''
  

Версия 23:02, 30 сентября 2013

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>()); // Ошибка