Обобщенные классы
Часть раздела Объектно-ориентированное программирование путешествия по 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();Обратите внимание на <> (ромбовидный оператор) в правой части — 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 + " -> " + giftMain.java: Оживите ваш реестр подарков! Вы получите три входных значения: имя получателя (String), описание подарка (String) и стоимость подарка (целое число, представляющее доллары).Сначала создайте
GiftBox<String>, содержащий описание подарка. Упакуйте его с помощью методаwrap(), затем выведите его статус.Затем создайте
GiftBox<Integer>, содержащий стоимость подарка (представляющую сумму подарочной карты). Не упаковывайте этот подарок — выведите его статус, чтобы показать, что он не упакован.Наконец, создайте
Registry<String, String>, связывающий имя получателя с описанием подарка, и выведите запись из реестра.
Вы получите три входных значения по порядку: имя получателя, описание подарка и стоимость подарка (в виде целого числа).
Обратите внимание, как один и тот же класс GiftBox беспрепятственно работает как с типом String, так и с типом Integer — в этом и заключается прелесть дженериков! А класс Registry демонстрирует, как использование нескольких параметров типа позволяет создавать гибкие пары между любыми двумя типами.
Попробуйте сами
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>, содержащий описание подарка (gift description)
// TODO: Упаковать его с помощью метода wrap()
// TODO: Вывести его статус с помощью getStatus()
// TODO: Создать GiftBox<Integer>, содержащий стоимость подарка (gift value)
// TODO: Не упаковывать этот подарок — вывести его статус (должен отображаться как неупакованный)
// 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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java