Menu
CoddyTech

Ограниченные параметры типа

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 56 из 87.

До сих пор наши параметры generic-типа могли принимать любой тип. Но что, если нужно их ограничить?

Например, method, вычисляющий сумму чисел, не должен принимать строки. Ограниченные параметры типов позволяют ограничить типы, которые можно использовать с вашими generic.

Используйте ключевое слово extends, чтобы задать верхнюю границу параметра типа:

class NumberBox<T extends Number> {
    private T value;
    
    public NumberBox(T value) {
        this.value = value;
    }
    
    public double getDoubleValue() {
        return value.doubleValue();  // Можно вызывать методы Number!
    }
}

NumberBox<Integer> intBox = new NumberBox<>(42);
NumberBox<Double> doubleBox = new NumberBox<>(3.14);
// NumberBox<String> strBox = new NumberBox<>("Hi");  // Ошибка компиляции!

Ограничение T extends Number означает, что T должен быть Number или любым из его подклассов (Integer, Double и т. д.). Это также позволяет вызывать methods, определённые в Number, для параметра типа.

Вы можете указать multiple ограничений с помощью &. Если одно из ограничений — class, оно должно стоять первым:

class DataProcessor<T extends Number & Comparable<T>> {
    public boolean isGreater(T a, T b) {
        return a.compareTo(b) > 0;
    }
}

Здесь T должен расширять Number И реализовывать Comparable. Ограниченные параметры типа также работают с обобщёнными методами:

public static <T extends Comparable<T>> T findMax(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

String result = findMax("apple", "banana");  // Возвращает "banana"

Ограниченные параметры типов делают ваши обобщения более мощными, обеспечивая безопасность типов и одновременно предоставляя доступ к определённым методам типа-ограничения.

challenge icon

Задание

Легко

Давайте создадим калькулятор статистики, который работает только с числовыми типами! Вы будете использовать bounded type parameters, чтобы гарантировать, что ваш калькулятор может обрабатывать только числа, получая при этом доступ к полезным методам из class Number.

Вы организуете свой код в трёх файлах:

  • NumberStats.java: Create generic class, вычисляющий статистические показатели для числовых значений. Ваш class NumberStats<T extends Number> должен хранить два private fields типа T: value1 и value2. Добавьте constructor, который accepts both значения. Добавьте method getSum(), возвращающий сумму both значений как double (используйте method doubleValue(), унаследованный от Number). Также добавьте method getAverage(), возвращающий среднее двух значений как double.
  • ComparableBox.java: Create generic class с multiple bounds, который может находить larger из двух значений. Ваш class ComparableBox<T extends Number & Comparable<T>> должен хранить два private fields типа T: first и second. Добавьте constructor, который accepts both значения. Добавьте method getMax(), возвращающий larger значение с использованием compareTo(). Также добавьте method getMaxAsDouble(), возвращающий larger значение, converted в double.
  • Main.java: Воплотите в жизнь свои bounded generics! Вы получите четыре inputs: два integer и два double.

    Сначала создайте NumberStats<Integer> с двумя integer значениями. Выведите сумму и среднее:

    Integer sum: [sum]
    Integer average: [average]

    Затем создайте NumberStats<Double> с двумя double значениями. Выведите сумму и среднее:

    Double sum: [sum]
    Double average: [average]

    Наконец, создайте ComparableBox<Integer> с двумя integer значениями и выведите:

    Max integer: [max]
    Max as double: [maxAsDouble]

Вы получите четыре inputs в следующем порядке: first integer, second integer, first double, second double.

Обратите внимание, как bound T extends Number позволяет вызвать doubleValue() для вашего generic типа. С unbounded type parameter это было бы невозможно. А multiple bounds в ComparableBox гарантируют, что вы сможете и преобразовать значение в double, и сравнить значения!

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

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Считать входные данные
        int int1 = scanner.nextInt();
        int int2 = scanner.nextInt();
        double double1 = scanner.nextDouble();
        double double2 = scanner.nextDouble();
        
        // TODO: Создайте NumberStats<Integer> с двумя целочисленными значениями
        // Выведите сумму и среднее значение в формате:
        // Integer sum: [sum]
        // Integer average: [average]
        
        // TODO: Создайте NumberStats<Double> с двумя значениями double
        // Выведите сумму и среднее значение в формате:
        // Double sum: [sum]
        // Double average: [average]
        
        // TODO: Создайте ComparableBox<Integer> с двумя целочисленными значениями
        // Выведите максимум и максимум в виде double в формате:
        // Max integer: [max]
        // Max as double: [maxAsDouble]
    }
}
quiz iconПроверьте себя

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

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

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