Зарегистрируйтесь, чтобы продолжить обучение

Реализация дженериков Java: Дженерики

Попрактикуемся в создании дженериков на примере такой коллекции как пара. Подобный тип данных встречается во многих языках и имитируется в Java с помощью мапы: Map.entry(key, value). Пара содержит два элемента, которые можно получить из пары и установить новые. Простейший пример использования пары – это точки на плоскости.

Один параметр типа

Начнем с примера. Ниже код использования пары как точки на плоскости.

var point = new SimplePair<Integer>();
point.setLeft(10);
point.setRight(20);

System.out.println(point.getLeft()); // 10
System.out.println(point.getRight()); // 20

Для простоты наша пара будет работать с одним параметром типа, который определяет тип обоих значений в паре. Напишем соответствующий класс:

public class SimplePair<T> {

    private T left;
    private T right;

    public T getLeft() {
        return left;
    }

    public T getRight() {
        return right;
    }

    public void setLeft(T left) {
        this.left = left;
    }

    public void setRight(T right) {
        this.right = right;
    }
}

Для того чтобы дженерики понимались проще, можно представлять дженерик как определение метода. В угловых скобках указывается имя параметра, который будет передан внутрь при "вызове дженерика". Внутри этот параметр используется уже без скобок. Даже в определении методов, которые ожидают данные этого типа как параметры.

Синтаксически в определении методов ничего не поменялось, кроме того, что вместо конкретного типа мы подставляем параметр типа T. В случае геттеров мы возвращаем данные этого типа, в случае сеттеров - получаем на вход.

Теперь создадим дженерик-интерфейс, по аналогии с интерфейсом List.

public interface Pair<T> {

    public T getLeft();
    public T getRight();

    public void setLeft(T left);
    public void setRight(T right);
}

И реализуем его.

public class SimplePair<T> implements Pair<T> {
    // тут реализация
}

Параметр типа указывается и для класса и для интерфейса. В остальном в классе ничего не меняется. Сделав такое изменение, мы получаем возможность подменять реализацию. В случае с парами это не очень актуально, но общая концепция становится понятна.

// Например пара, хранящаяся в базе данных
public class SqlitePair<T> implements Pair<T> {
    // тут реализация
}

Два параметра типа

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

// Первый параметр отвечает за left
// Второй параметр отвечает за right
var pair = new SimplePair<String, Integer>();
pair.setLeft("hexlet");
pair.setRight(100);

System.out.println(pair.getLeft()); // hexlet
System.out.println(pair.getRight()); // 100

Сначала поправим интерфейс.

public interface Pair<L, R> {

    public L getLeft();
    public R getRight();

    public void setLeft(L left);
    public void setRight(R right);
}

Два параметра типа выглядят как параметры в определении метода. В этот раз, для удобства восприятия, взяты имена L и R. Порядок параметров типов мы определяем сами, в данном случае логично разместить слева параметр отвечающий за left, а справа отвечающий за right.

Следующий шаг поменять класс.

public class SimplePair<L, R> {

    private L left;
    private R right;

    public L getLeft() {
        return left;
    }

    public R getRight() {
        return right;
    }

    public void setLeft(L left) {
        this.left = left;
    }

    public void setRight(R right) {
        this.right = right;
    }
}

И последнее, что мы сделаем, добавим конструктор, который позволяет передавать значения для пары в одну строку. Кроме того, в таком случае, компилятор автоматически выводит тип передаваемых значений, что позволяет не прописывать явно параметры типов.

var point = new SimplePair<>(10, 20);
point.getLeft(); // 10
point.getRight(); // 20

Реализация:

public class SimplePair<L, R> implements Pair<L, R> {

    private L left;
    private R right;

    // Добавляем конструктор по умолчанию
    // чтобы оставить возможность создавать пустую пару
    public SimplePair() {}
    public SimplePair(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public L getLeft() {
        return left;
    }

    public R getRight() {
        return right;
    }

    public void setLeft(L left) {
        this.left = left;
    }

    public void setRight(R right) {
        this.right = right;
    }
}

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Программирование на Java, Разработка веб-приложений и микросервисов используя Spring Boot, проектирование REST API
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»