Кто не встречал в коде и не использовал generics сам? Общая идея в том, что типы (классы или интерфейсы) могут выступать в роли параметров при определении классов, интерфейсов или методов. Generics - своего рода формальный параметр, при котором фактическим параметром будет не значение, а тип.
Преимущества очевидны:
Хотим создать generic type - обобщенный класс или интерфейс. Чтобы использовать generic, мы создаем generic type declaration: эту самую <T>. После того как мы ввели type variable, можно использовать её в классе.
У параметров есть naming convention:
Начиная с Java SE 7 можно опустить тип при создании. Удобно, если строка и так длинная:
Generic метод - метод с параметрами, но область их видимости ограничена телом метода. Благодаря type inference не обязательно указывать фактические типы:
Bounded Type Parameters (Ограничения)
Допустим, в качестве фактического параметра мы хотели бы видеть не каждый класс.
Ограничения используются для объявления типа и для объявления обобщенного метода. Ведь мы и так можем добавить Integer в List<Number>.
Wildcards (Маски)
В обобщенных типах знак вопроса (?) называется wildcard. Зачем оно надо?
Upper bounder wildcard. Чтобы метод работал со списком Number или его потомков, например, списком Integer, формальным параметром метода должен быть List<? extends Number>. Это менее строго, чем List<Number>, потому что компилятор не пример List<Integer> как фактический параметр (потому что List<Integer> не является потомком List<Number>)
Но за гибкость надо платить. Теперь это список объектов, неизвестного типа:
Unbounded wildcard. Конструкция List<?> называется list of unknown type. Используется, когда:
1. Метод может быть реализован, используя функциональность Object'а. Например, toString().
2. Метод в обобщенном классе не зависит от типа параметра. Например, list.size() на зависит от типа объектов в list'е.
Lower bounded wildcards. Позволяет ограничивать возможный тип снизу. Допустим, нам надо добавить Integer в список. Тогда мы хотели бы, чтобы список был List<Integer>, List<Number> или List<Object>:
Мораль
Не путать List<Object> / List<?> / List. При использовании List мы теряем type safety, потому что не знаем List каких элементов прячется в списке. List<?> используем, если не знаем список каких элементов будет фактическим параметром. List<Object> используем, если в нашем списке будут элементы различных типов.
Не путать List<? extends Number> и <T extends Number>. Смысл List<? extends Number> в том, что мы не знаем список какого типа будет фактическим параметром. Смысл <T extends Number> в том, что мы хотим, чтобы обобщенный метод или класс работал с потомками класса Number или им самим. При объявлении обобщенного класса или метода используется второе. Первое используется как формальный аргумент метода. Если мы хотим нарисовать фигуры, то разницы между
Например, первый вариант не даст нам добавить новый элемент в список:
Преимущества очевидны:
- Проверка типов времени компиляции
- Меньше приведений типов
- Можно реализовать более общий алгоритм для коллекций объектов разных типов, который будет легким для чтения и, в то же время, type safe.
Хотим создать generic type - обобщенный класс или интерфейс. Чтобы использовать generic, мы создаем generic type declaration: эту самую <T>. После того как мы ввели type variable, можно использовать её в классе.
/** * Generic version of the Box class. * @param <T> the type of the value being boxed */ public class Box<T> { // T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
У параметров есть naming convention:
- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
- S,U,V etc. - 2nd, 3rd, 4th types
Начиная с Java SE 7 можно опустить тип при создании. Удобно, если строка и так длинная:
Box<Integer> integerBox = new Box<>(); OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));
Generic метод - метод с параметрами, но область их видимости ограничена телом метода. Благодаря type inference не обязательно указывать фактические типы:
boolean same = Util.<Integer, String> compare(p1, p2); boolean same = Util.compare(p1, p2); public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {}
Bounded Type Parameters (Ограничения)
Допустим, в качестве фактического параметра мы хотели бы видеть не каждый класс.
public class NaturalNumber<T extends Integer> { public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {Возможно множественное ограничение, но если в списке есть класс, он должен идти ДО интерфейсов:
<T extends B1 & B2 & B3> // B1 - class, B2,B3 - interfaces
Ограничения используются для объявления типа и для объявления обобщенного метода. Ведь мы и так можем добавить Integer в List<Number>.
Wildcards (Маски)
В обобщенных типах знак вопроса (?) называется wildcard. Зачем оно надо?
Upper bounder wildcard. Чтобы метод работал со списком Number или его потомков, например, списком Integer, формальным параметром метода должен быть List<? extends Number>. Это менее строго, чем List<Number>, потому что компилятор не пример List<Integer> как фактический параметр (потому что List<Integer> не является потомком List<Number>)
Но за гибкость надо платить. Теперь это список объектов, неизвестного типа:
public void addRectangle(List<? extends Shape> shapes) { // Compile-time error! shapes.add(0, new Rectangle()); }
Unbounded wildcard. Конструкция List<?> называется list of unknown type. Используется, когда:
1. Метод может быть реализован, используя функциональность Object'а. Например, toString().
public static void printList(List<?> list) { for (Object elem: list) System.out.print(elem + " "); System.out.println(); }Важно понимать, что List<Object> и List<?> - не одно и тоже. В List<Object> можно добавить объект любого типа. В List<?> можно добавить только null. C другой стороны, в printList(List<Object> l) невозможно передать List<Integer> - это ошибка времени компиляции.
2. Метод в обобщенном классе не зависит от типа параметра. Например, list.size() на зависит от типа объектов в list'е.
Lower bounded wildcards. Позволяет ограничивать возможный тип снизу. Допустим, нам надо добавить Integer в список. Тогда мы хотели бы, чтобы список был List<Integer>, List<Number> или List<Object>:
public static void addNumbers(List<? super Integer> list) { for (int i = 1; i <= 10; i++) { list.add(i); } }
Мораль
Не путать List<Object> / List<?> / List. При использовании List мы теряем type safety, потому что не знаем List каких элементов прячется в списке. List<?> используем, если не знаем список каких элементов будет фактическим параметром. List<Object> используем, если в нашем списке будут элементы различных типов.
Не путать List<? extends Number> и <T extends Number>. Смысл List<? extends Number> в том, что мы не знаем список какого типа будет фактическим параметром. Смысл <T extends Number> в том, что мы хотим, чтобы обобщенный метод или класс работал с потомками класса Number или им самим. При объявлении обобщенного класса или метода используется второе. Первое используется как формальный аргумент метода. Если мы хотим нарисовать фигуры, то разницы между
public void drawAll(List<? extends Shape> shapes){и
public <T extends Shape> void drawAll(List<T> shapes){нет. Но, давая имя типу, мы можем использовать это имя в нашем методе.
Например, первый вариант не даст нам добавить новый элемент в список:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) { if (shape.isPretty()) { shapes.add(shape); } }
Comments
Post a Comment