Обобщенные классы
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 54 из 87.
Теперь, когда вы понимаете, почему обобщения полезны, давайте создадим ваш собственный обобщенный класс. Обобщенный класс объявляет один или несколько параметров типа в угловых скобках после имени класса, которые затем могут использоваться во всем классе.
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}T — это параметр типа, заполнитель, который заменяется фактическим типом при создании экземпляра. Вы можете использовать любое имя, но общепринято использовать одиночные заглавные буквы: T для Type (Тип), E для Element (Элемент), K для Key (Ключ), V для Value (Значение).
При создании экземпляра универсального класса вы указываете фактический тип в угловых скобках:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // Приведение типов не требуется
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();Обратите внимание на <> (diamond-оператор) в правой части — Java выводит тип из объявления. Вы также можете использовать несколько параметров типа для классов, которым необходимо работать с более чем одним типом:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Задание
ЛегкоДавайте создадим систему реестра подарков, которая продемонстрирует мощь универсальных классов (generics). Вы создадите гибкую систему хранения, способную содержать любой тип подарка, а также реестр, который связывает имена получателей с деталями подарков, используя несколько параметров типа.
Вы организуете свой код в трех файлах:
GiftBox.java: Создайте универсальный класс, который может упаковать любой тип подарка. ВашGiftBox<T>должен иметь приватное полеitemтипаTи приватное полеwrapped(boolean), которое изначально равноfalse. Добавьте конструктор, принимающий предмет, методgetItem(), возвращающий предмет, методwrap(), который устанавливает wrapped в true, и методisWrapped(), возвращающий статус упаковки. Добавьте методgetStatus(), который возвращает либо[item] (wrapped), либо[item] (unwrapped)в зависимости от состояния упаковки.Registry.java: Создайте универсальный класс с двумя параметрами типа, который связывает получателя с его подарком. ВашRegistry<K, V>должен иметь приватные поляrecipientтипаKиgiftтипаV. Добавьте конструктор, принимающий оба значения, методы-геттерыgetRecipient()иgetGift(), а также методgetEntry(), который возвращает:[recipient] -> [gift]Main.java: Оживите ваш реестр подарков! Вы получите три входных значения: имя получателя (String), описание подарка (String) и стоимость подарка (целое число, представляющее доллары).Сначала создайте
GiftBox<String>, содержащий описание подарка. Упакуйте его с помощью методаwrap(), затем выведите его статус.Затем создайте
GiftBox<Integer>, содержащий стоимость подарка (представляющую сумму подарочной карты). Не упаковывайте этот подарок — выведите его статус, чтобы показать, что он не упакован.Наконец, создайте
Registry<String, String>, связывающий имя получателя с описанием подарка, и выведите запись реестра.
Вы получите три входных значения по порядку: имя получателя, описание подарка и стоимость подарка (в виде целого числа).
Обратите внимание, как один и тот же класс GiftBox беспрепятственно работает как с типом String, так и с типом Integer — в этом и заключается прелесть дженериков! А класс Registry демонстрирует, как использование нескольких параметров типа позволяет создавать гибкие пары между любыми двумя типами.
Шпаргалка
Обобщенный класс (generic class) объявляет один или несколько параметров типа в угловых скобках после имени класса, которые могут использоваться во всем классе:
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}T — это параметр типа — заполнитель, заменяемый реальным типом при создании экземпляра. По общепринятым соглашениям используются одиночные заглавные буквы: T для типа (Type), E для элемента (Element), K для ключа (Key), V для значения (Value).
При создании экземпляра обобщенного класса укажите фактический тип в угловых скобках:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // No casting needed
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();Оператор <> (diamond operator) позволяет Java выводить тип из объявления.
Используйте несколько параметров типа для классов, которые работают с более чем одним типом:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Чтение входных данных
String recipientName = scanner.nextLine();
String giftDescription = scanner.nextLine();
int giftValue = scanner.nextInt();
// TODO: Создайте GiftBox<String>, содержащий описание подарка
// TODO: Упакуйте его с помощью метода wrap()
// TODO: Выведите его статус с помощью getStatus()
// TODO: Создайте GiftBox<Integer>, содержащий стоимость подарка
// TODO: Не упаковывайте этот подарок — выведите его статус (должно отобразиться unwrapped)
// TODO: Создайте Registry<String, String>, связывающий получателя с описанием подарка
// TODO: Выведите запись реестра с помощью getEntry()
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»