Попрактикуемся в создании дженериков на примере такой коллекции как пара. Подобный тип данных встречается во многих языках и имитируется в 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;
}
}
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.