3_Обобщения.pptx
- Количество слайдов: 18
Generics
Обобщённое программирование (англ. generic programming, GP) — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание. GP - методология программирования, основанная на разделении структур данных и алгоритмов через использование абстрактных описаний требований. Вместо описания отдельного типа в обобщённом программировании применяется описание семейства типов, имеющих общий интерфейс и семантическое поведение (англ. semantic behavior). Набор требований, описывающий интерфейс и семантическое поведение, называется концепцией (англ. concept). Обобщённое программирование в Java – контейнеры типа T (generics). Основное применение – collections framework (каркас коллекций).
Пример Объявление class Gen<T> { //T – type parameter T val; Gen(T val) { this. val = val; } Использование class Main { public static void main(String. . . args) Gen<Integer> a = new Gen<>(221); Gen<String> b = new Gen<>("B"); //Gen – parametrized type //Integer, String – concrete type parameters a. show. Value(); b. show. Value(); void show. Value() { print("Value is: " + val. to. String()); } } {
Ограниченные типы //метод, вычисляющий сумму аргументов //находится внутри какого-то класса static <T> double sum(T. . . args) { double res = 0; for (T val : args) { res += val. double. Value(); //cannot resolve method } } return res;
Ограниченные типы //метод, вычисляющий сумму аргументов //находится внутри какого-то класса static <T extends Number> double sum(T. . . args) { double res = 0; for (T val : args) { res += val. double. Value(); //cannot resolve method } } return res;
Ограниченные типы //метод, вычисляющий сумму аргументов //находится внутри какого-то класса static <T extends Number> double sum(T. . . args) { double res = 0; for (T val : args) { res += val. double. Value(); //cannot resolve method } } return res; Про конструкцию T extends Number говорят: тип T ограничен сверху классом Number. Про конструкцию T super Number говорят: тип T ограничен снизу классом Number.
Применение метасимвольных аргументов (wildcards) //метод, сравнивающий два параметризированных объекта //находится внутри какого-то класса class A <T extends Number> { T val; A(T val) { this. val = val; } } public boolean eq(A<T> arg) { return val == arg. val; } //emmm…
Применение метасимвольных аргументов (wildcards) //метод, сравнивающий два параметризированных объекта //находится внутри какого-то класса class A <T extends Number> { T val; A(T val) { this. val = val; } } public boolean eq(A<? > arg) { return val == arg. val; }
Вариантность Параметризированный тип Gen<T> называют ковариантным типу T, если из T extends K => Gen<T> extends Gen<K> Параметризированный тип Gen<T> называют контравариантным типу T, если из T extends K => Gen<T> super Gen<K> Параметризированный тип Gen<T> называется инвариантным типу T, если отношение наследования исходных типов T, К не переносится на производные Gen<T>, Gen<K>.
Ограниченные метасимвольные аргументы public static void show. XY(Coords<? > c) { for (Two. D coord : c. coords) { print("x " + coord. x + " y " + coord. y); } } public static void show. XYZ(Coords<? extends Three. D> c) { for (Three. D coord : c. coords) { print("x " + coord. x + " y " + coord. y + " z " + coord. z); } }
PECS principle public abstract class My. Object. Store<K, V> { abstract void put(K key, V value); void put. All(Map<K, V> entries); Map<K, V> get. All(Collection<K> keys); Collection<V> get. All(Predicate<V> p); } //использование My. Object. Store<Long, Vehicle> cars. Store =. . . ; Map<Long, Car> cars = new Hash. Map<Long, Car>(2); cars. put(1 L, new Car("Audi", "A 6", 2011)); cars. put(2 L, new Car("Honda", "Civic", 2012)); cars. Store. put. All(cars); // Ошибка компиляции.
Формулировка Если метод имеет аргументы с параметризованным типом (например, Collection<T> или Predicate<T>), то в случае, если аргумент — производитель (producer), нужно использовать ? extends T, а если аргумент — потребитель (consumer), нужно использовать ? super T. Eсли метод читает данные из аргумента, то этот аргумент — производитель, а если метод передаёт данные в аргумент, то аргумент является потребителем. Важно заметить, что определяя производителя или потребителя, мы рассматриваем только данные типа T.
С исправлениями public abstract class My. Object. Store<K, V> { abstract void put(K key, V value); void put. All(Map<? extends K, ? extends V> entries); Map<K, V> get. All(Collection<? extends K> keys); Collection<V> get. All(Predicate<? super V> p); } //использование My. Object. Store<Long, Vehicle> cars. Store =. . . ; Map<Long, Car> cars = new Hash. Map<Long, Car>(2); cars. put(1 L, new Car("Audi", "A 6", 2011)); cars. put(2 L, new Car("Honda", "Civic", 2012)); cars. Store. put. All(cars); // Корректно
Особенности параметризации public class Traverse. Struct<T> { public Array. List<Subtree. Position> path; //. . . } Array. List<Traverse. Struct> store; for (Traverse. Struct struct : store) { //. . for (Subtree. Position pos : struct. path); //incompatible types: java. lang. Object //cannot be converted to Subtree. Position }
Особенности параметризации Eсли класс параметризирован, но мы начинаем использовать его без параметра, компилятор перестает принимать во внимание все параметризованные типы внутри этого класса, даже те, которые не относятся к параметру самого класса. Array. List<Traverse. Struct> store; for (Traverse. Struct<? > struct : store) { //. . for (Subtree. Position pos : struct. path); //incompatible types: java. lang. Object //cannot be converted to Subtree. Position }
Стирание типов (type erasure) Во время компиляции информация о параметрах типов стирается Вы не можете получить информацию о параметрах типов в runtime Выделение памяти для объектов имеющих тип параметра невозможно Во время компиляции обобщения заменяются на явное приведение Следовательно, параметризированные типы, по сути, являются одним и тем же типом: Одним из побочных эффектов механизма стирания является неспособность класса реализовать оба интерфейса Comparable<String> и Comparable<Number>, поскольку оба они на самом деле являются одним и тем же интерфейсом, указывая на один и тот же метод compare. To().
Итог Обобщения действуют только со ссылочными типами Обобщённые типы различаются по аргументам типа Стирание типов (Type Erasure) Обобщения обеспечивают безопасность типов времени компиляции При наследовании обобщённых типов любые аргументы типа, требующиеся обобщенному суперклассу, должны передаваться всеми подклассами вверх по иерархии Diamond operator Потеря параметризации
3_Обобщения.pptx