Generics
Jav a
МОТИВАЦИЯ
Java Generics
2
ХРАНИЛИЩЕ ЭЛЕМЕНТОВ
public class Example01 {
public static void main(String[] args) {
ArrayList test = new ArrayList();
[Link]("test");
[Link](3);
[Link](3.0);
[Link]((Object)2);
for (int i = 0; i < [Link](); i++)
[Link]([Link](i));
}
}
3
ХРАНИЛИЩЕ ЭЛЕМЕНТОВ
public class Example01 {
public static void main(String[] args) {
ArrayList test = new ArrayList();
[Link]("test"); Результат:
[Link](3); test
3
[Link](3.0); 3.0
[Link]((Object)2); 2
for (int i = 0; i < [Link](); i++)
[Link]([Link](i));
}
}
4
ХРАНИЛИЩЕ ЭЛЕМЕНТОВ
public class Example01 {
public static void main(String[] args) {
ArrayList test = new ArrayList();
[Link]("test");
[Link](3);
[Link](3.0);
[Link]((Object)2);
for (int i = 0; i < [Link](); i++)
[Link]((String)[Link](i));
}
}
5
ХРАНИЛИЩЕ ЭЛЕМЕНТОВ
public class Example01 {
public static void main(String[] args) {
ArrayList test = new ArrayList();
Результат: [Link]("test");
test [Link](3);
Exception in thread "main" [Link]:
[Link](3.0);
[Link] cannot be cast to [Link]
[Link]((Object)2);
at [Link]([Link])
for (int i = 0; i < [Link](); i++)
[Link]((String)[Link](i));
}
}
6
ХРАНИЛИЩЕ ЭЛЕМЕНТОВ
public class Example02 {
public static void main(String[] args) {
ArrayList<String> test = new ArrayList<String>();
[Link]("test"); Ошибка компиляции:
[Link](3);
The method add(String) in the
type ArrayList<String> is not
[Link](3.0);
applicable for the arguments
[Link]((Object)2);
(double)
for (int i = 0; i < [Link](); i++)
[Link]([Link](i));
}
}
7
ВНУТРИ ХРАНИЛИЩА
class Holder1 {
private Automobile a;
public Holder1(Automobile a) { this.a = a; }
Automobile get() { return a; }
}
Этот контейнер не универсален – позволяет
хранить только Automobile
8
ВНУТРИ ХРАНИЛИЩА-2
class Holder2 {
private Object a;
public Holder2(Object a) { this.a = a; }
public void set(Object a) { this.a = a; }
public Object get() { return a; }
}
Этот контейнер универсален – но не удобен в использовании и нет
защиты от неверного типа данных
Holder2 h2 = new Holder2(new Automobile());
Automobile a2 = (Automobile)[Link]();
[Link]("Not an Automobile");
String s = (String)[Link]();
[Link](1); // Автоматически упаковывается в Integer
Integer x = (Integer)[Link]();
Именно так были реализованы контейнеры до Java 5
9
ВНУТРИ ХРАНИЛИЩА-3
class Holder3<T> {
private T a;
public Holder3(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
}
public class Example04 {
public static void main(String[] args) {
Holder3<Automobile> h3 = new Holder3<Automobile>(new
Automobile());
Automobile a = [Link](); // Преобразование не требуется
[Link]("Not an Automobile");// Ошибка
[Link](1); // Ошибка
}
}
Один класс на любой тип объекта + не требуется преобразование +
защита от неверного типа данных 10
GENERICS
Java Generics
11
КАК УСТРОЕНО
• В С++ шаблоны компилируются в новый класс
при использовании каждого нового типа
параметра. В итоге имеем отдельный
машинный код для каждого типа параметра.
• В Java в скомпилированном байт-коде нет
никакой информации о типе параметра. Один
байт-код для всех типов параметра.
• При компиляции вся информация о типе
параметра стирается и превращается в
наиболее общий тип – Object, если нет
дополнительных ограничений.
12
КАК РАБОТАЕТ (на примере ArrayList)
• В байт-коде класса ArrayList написано, что он
внутри себя хранит массив Object[] (а не T[])
• Данный код:
ArrayList<String> al = new ArrayList<String>();
[Link]("Mama");
String tmp = [Link](0);
• Преобразуется в:
ArrayList al = new ArrayList();
[Link]((String)"Mama");
String tmp = (String)[Link](0);
13
КАК РАБОТАЕТ
• Таким образом:
• generic‘и позволяют работать единым образом с
переменными различных типов
• generic-код при компиляции теряет всю информацию о
типе
• по своей сути generic-класс – это обертка над классом
с Object, которая проверяет (приводит) тип во всех
точках входа и выхода из generic-кода. Тем самым
проверяется корректность присваиваний и операций на
этапе компиляции
• для проверки корректности на этапе выполнения
существуют, например, специальные коллекции.
14
НЕСОВМЕСТИМОСТЬ GENERIC-ТИПОВ
• Generic-типы не совместимы по присваиванию
List<Integer> li = new ArrayList<Integer>();
List<Object> lo = li;
• Иначе — ошибки
[Link](“hello”); // ClassCastException
Integer li = [Link](0);
15
ПРЕОБРАЗОВАНИЕ ТИПОВ
• Уничтожение информации о типе
• List l = new ArrayList<String>();
• Добавление информации о типе
• List<String> l = (List<String>) new ArrayList();
• List<String> l1 = new ArrayList();
• Unchecked warning
• Ответственность программиста
• SuppressWarnings("unchecked")
16
ОГРАНИЧЕНИЯ GENERIC
• Невозможно создать массив параметра типа
• Collection<T> c;
• T[] ta;
• new T[10];
• Невозможно создать массив Generic-классов
• new ArrayList<List<Integer>>();
• List<?>[] la = new List<?>[10];
Но этот код работает (правда с unchecked):
• List<String>[] la = new List[10];
17
ПРИМЕРЫ
ИСПОЛЬЗОВАНИЯ
Java Generics
18
ПРОБЛЕМА 1
• Метод
void dump(Collection<Object> c) {
for (Iterator<Object> i = [Link](); [Link](); ) {
Object o = [Link]();
[Link](o);
}
}
• Вызовы
• List<Object> l; dump(l);
• List<Integer> l; dump(l);
РЕШЕНИЕ 1 – WILDCARD
• Метод
void dump(Collection<?> c) {
for (Iterator<?> i = [Link](); [Link](); ) {
Object o = [Link]();
[Link](o);
}
}
• Вызовы
• List<Object> l; dump(l);
• List<Integer> l; dump(l);
ПРОБЛЕМА 2
• Метод
void draw(List<Shape> c) {
for (Iterator<Shape> i = [Link](); [Link](); ) {
Shape s = [Link]();
[Link]();
}
}
• Вызовы
• List<Shape> l; draw(l);
• List<Circle> l; draw(l);
РЕШЕНИЕ 2 – BOUNDED WILDCARD
• Метод
void draw(List<? extends Shape> c) {
for (Iterator<? extends Shape> i = [Link]();
[Link](); ) {
Shape s = [Link]();
[Link]();
}
}
• Вызовы
• List<Shape> l; draw(l);
• List<Circle> l; draw(l);
ПРОБЛЕМА 3
• Метод
void addAll(Object[] a, Collection<?> c) {
for (int i = 0; i < [Link]; i++) {
[Link](a[i]);
}
}
• Примеры использования
• addAll(new String[10], new ArrayList<String>());
• addAll(new Object[10], new ArrayList<Object>());
• addAll(new String[10], new ArrayList<Object>());
• addAll(new Object[10], new ArrayList<String>());
РЕШЕНИЕ 3 – GENERIC-МЕТОД
• Метод
<T> void addAll(T[] a, Collection<T> c) {
for (int i = 0; i < [Link]; i++) {
[Link](a[i]);
}
}
• Примеры использования
• addAll(new String[10], new ArrayList<String>());
• addAll(new Object[10], new ArrayList<Object>());
• addAll(new String[10], new ArrayList<Object>());
• addAll(new Object[10], new ArrayList<String>());
ПРОБЛЕМА 4
• Метод
<T> void addAll(Collection<T> c, Collection<T> c2) {
for (Iterator<T> i = [Link](); [Link](); ) {
T o = [Link]();
[Link](o);
}
}
• Примеры использования
• addAll(new AL<Integer>(), new AL<Integer>());
• addAll(new AL<Integer>(), new AL<Object>());
• addAll(new AL<Object>(), new AL<Integer>());
РЕШЕНИЕ 4 – BOUNDED TYPE
ARGUMENT
• Метод
<T, S extends T> void addAll(Collection<S> c, Collection<T>
c2) {
for (Iterator<S> i = [Link](); [Link](); ) {
S o = [Link]();
[Link](o);
}
}
• Примеры использования
• addAll(new AL<Integer>(), new AL<Integer>());
• addAll(new AL<Integer>(), new AL<Object>());
РЕШЕНИЕ 4’ – BOUNDED WILDCARD
• Метод
<T> void addAll(Collection<? extends T> c, Collection<T> c2) {
for (Iterator<? extends T> i = [Link](); [Link]();
){
T o = [Link]();
[Link](o);
}
}
• Примеры использования
• addAll(new AL<Integer>(), new AL<Integer>());
• addAll(new AL<Integer>(), new AL<Object>());
ПРОБЛЕМА 5
• Метод
<T extends Comparable<T>>
T max(Collection<T> c) {
}
• Пример использования
• List<Integer> il; Integer I = max(il);
• class Test implements Comparable<Object> {…}
List<Test> tl; Test t = max(tl);
РЕШЕНИЕ 5 – UPPER BOUNDED WCARD
• Метод
<T extends Comparable<? super T>>
max(Collection<T> c) {
…
}
• Пример использования
• List<Integer> il; Integer I = max(il);
• class Test implements Comparable<Object> {…}
List<Test> tl; Test t = max(tl);
WILDCARD CAPTURE (1)
void swap(List<?> list, int i, int j) {
// ?
}
Не работает:
void swap(List<?> list, int i, int j) {
// Несоответствие типов
[Link](i, [Link](j));
}
WILDCARD CAPTURE (2)
void swap(List<?> list, int i, int j) {
swapImpl(list, i, j);
}
<T> void swapImpl(List<T> list, int i, int j) {
T temp = [Link](i);
[Link](i, [Link](j));
[Link](j, temp);
}