Menu
Coddy logo textTech

Обобщенные классы

Часть раздела Объектно-ориентированное программирование путешествия по 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);
challenge icon

Задание

Легко

Давайте создадим систему реестра подарков, которая продемонстрирует мощь универсальных классов (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 демонстрирует, как использование нескольких параметров типа позволяет создавать гибкие пары между любыми двумя типами.

Попробуйте сами

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()
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java