Skip to main content

Попурри о Generics

Кто не встречал в коде и не использовал generics сам? Общая идея в том, что типы (классы или интерфейсы) могут выступать в роли параметров при определении классов, интерфейсов или методов. Generics - своего рода формальный параметр, при котором фактическим параметром будет не значение, а тип.
Преимущества очевидны:

  1. Проверка типов времени компиляции
  2. Меньше приведений типов
  3. Можно реализовать более общий алгоритм для коллекций объектов разных типов, который будет легким для чтения и, в то же время, type safe.
Generic ("Обобщенные" в русской традиции) типы и методы

Хотим создать 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

Popular posts from this blog

Блочный, строчный, ...

Сборник советов: 1. Прижать элемент к нижнему краю контейнера: 2 . Две колонки: 3 . Что означает символ ">" в селекторе? Означает, что будут выбраны только прямые потомки. По-умному, direct descendant combinator. Иными словами, если есть: То "div a {color: blue;}" изменит цвет всех ссылок, а "div > a {color: blue;}" - только первой и третьей. 4 . TODO

Hibernate

1. Hibernate Session . Javadoc называет её главным интерфейсом между java-приложением и Hibernate. Иногда её называют единицей работы или логической транзакцией. Основная функция сессии - обеспечить операции создания, чтения, удаления объектов. Объекты могут находиться в одном из 3 состояний (см. 2) Сессия создается с помощью SessionFactory. SessionFactory знает конфигурацию ORM, её внутреннее состояния immutable. При вызове Configuration().configure() загружается файл hibernate.cfg.xml. После того, как конфигурация загружена, можно изменить настройки программно. Данные корректировки возможны только до создания экземпляра фабрики сессий. // Initialize the Hibernate environment Configuration cfg = new Configuration().configure(); // Create the session factory ServiceRegistryBuilder srb = new ServiceRegistryBuilder(); ServiceRegistry sr = srb.applySettings(cfg.getProperties()).buildServiceRegistry(); SessionFactory factory = cfg.buildSessionFactory(sr); // Obtain the new session obj...

Первые шаги в Selenium

Цель расплывчата: понять и сделать хоть что-нибудь =) Selenium Selenium - группа продуктов. Отличное описание в статье Что такое Selenium? Selenium IDE - плагин для Firefox, позволяет записать тест, выполняя действия в браузере. Казалось бы, Selenium WebDriver должен дать больше возможностей: всё таки не из графического интерфейса тесты пишем, а с помощью языка программирования, но интересная мысль в другой статье того же автора  Что такое Selenium WebDriver? : "... WebDriver не имеет прямого отношения к тестированию. Он всего лишь предоставляет автотестам доступ к браузеру. На этом его функции заканчиваются.Структурирование, группировку и запуск тестов, а также генерацию отчётов о тестировании, обеспечивает фреймворк тестирования, такой как JUnit или TestNG для Java, .."  Локаторы Выбрать элемент на странице можно используя локаторы. Локаторы (в Selenium IDE в поле target) используют DOM, XPath или CSS (или просто ищут по id/name). Полезное: XPath: XML Path La...