Обобщения

Базовый Java
≈ 2 мин чтения

Тип как параметр

Обобщения позволяют написать класс, интерфейс или метод так, чтобы конкретный тип данных передавался ему как параметр. Вместо контейнера, который хранит «что угодно», можно явно сказать: здесь лежат строки, числа или объекты конкретного класса.

List<String> names = new ArrayList<>();
names.add("Mila");
names.add("Artem");

String first = names.get(0);

Запись List<String> означает: этот список работает со значениями типа String. Компилятор не даст добавить в него Integer, а при чтении не понадобится вручную приводить результат к String.

Зачем это нужно

Без обобщений ошибка типа могла бы проявиться только во время выполнения. С обобщениями большая часть таких ошибок останавливается ещё на этапе компиляции. Код одновременно становится безопаснее и понятнее: тип элемента виден прямо в объявлении переменной.

List<Integer> scores = new ArrayList<>();
scores.add(10);
scores.add(20);

// scores.add("30"); // compilation error

Справа тип часто можно не повторять. Оператор <> называют diamond operator: компилятор выводит параметр типа из левой части выражения.

Собственный обобщённый класс

Параметр типа можно использовать и в своих классах. Обычно его обозначают коротким именем вроде T — type.

class Box<T> {
    private T value;

    Box(T value) {
        this.value = value;
    }

    T get() {
        return value;
    }
}

Box<String> box = new Box<>("Java");
String value = box.get();

Класс Box написан один раз, но может работать с разными ссылочными типами. При создании Box<String> параметр T для этого объекта воспринимается как String.

Обобщённые методы

Параметр типа может принадлежать не всему классу, а отдельному методу. Он объявляется перед типом возвращаемого значения.

static <T> T first(T[] values) {
    return values[0];
}

String name = first(new String[]{"Ann", "Bob"});
Integer number = first(new Integer[]{10, 20});

Один метод работает с разными типами, но сохраняет связь между типом аргумента и типом результата. Именно это отличает обобщения от работы через слишком общий Object.

Параметрами обобщений могут быть только ссылочные типы. Поэтому пишут List<Integer>, а не List<int>. Для примитивов используются их классы-обёртки.