← Назад к вопросам

Как ограничить параметризованный тип данных

2.0 Middle🔥 171 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ограничение параметризованных типов (Generics Bounds)

Ограничения типов (Type Bounds) позволяют ограничить, какие типы могут быть использованы как параметры для дженериков. Это обеспечивает типобезопасность и позволяет компилятору проверить корректность использования.

1. Upper Bounded Wildcards (Верхняя граница)

Синтаксис: <T extends ClassName>

// T должен быть классом Number или его подклассом
public class Calculator<T extends Number> {
    private T value;
    
    public Calculator(T value) {
        this.value = value;
    }
    
    public double doubleValue() {
        return value.doubleValue();  // Метод Number
    }
    
    public int intValue() {
        return value.intValue();  // Метод Number
    }
}

// Использование
Calculator<Integer> intCalc = new Calculator<>(42);
Calculator<Double> doubleCalc = new Calculator<>(3.14);
// Calculator<String> error = new Calculator<>("text");  // ОШИБКА!

2. Множественные границы

Тип может быть ограничен сразу несколькими интерфейсами и классом:

// T должен быть Comparable И иметь Serializable
public class PairComparable<T extends Number & Comparable<T> & Serializable> {
    private T first;
    private T second;
    
    public T max() {
        return first.compareTo(second) > 0 ? first : second;
    }
}

// Класс, удовлетворяющий всем условиям
class MyNumber extends Number implements Comparable<MyNumber>, Serializable {
    private int value;
    
    @Override
    public int intValue() { return value; }
    
    @Override
    public long longValue() { return (long) value; }
    
    @Override
    public float floatValue() { return (float) value; }
    
    @Override
    public double doubleValue() { return (double) value; }
    
    @Override
    public int compareTo(MyNumber other) {
        return Integer.compare(this.value, other.value);
    }
}

3. Wildcard с верхней границей

Используется в параметрах методов:

public class Collection {
    
    // Принимает любой список, содержащий Number или его подклассы
    public static double sum(List<? extends Number> numbers) {
        double total = 0;
        for (Number n : numbers) {
            total += n.doubleValue();
        }
        return total;
    }
    
    // Использование
    List<Integer> integers = Arrays.asList(1, 2, 3);
    List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
    
    System.out.println(sum(integers));  // 6.0
    System.out.println(sum(doubles));   // 7.5
}

4. Lower Bounded Wildcards (Нижняя граница)

Синтаксис: <? super ClassName>

public class Producer {
    
    // Можно добавить Integer или его супер-классы
    public static void fillWithIntegers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
    }
    
    // Использование
    List<Number> numbers = new ArrayList<>();
    List<Object> objects = new ArrayList<>();
    
    fillWithIntegers(numbers);  // OK - Number это супер-класс Integer
    fillWithIntegers(objects);  // OK - Object это супер-класс Integer
    
    List<Integer> integers = new ArrayList<>();
    // fillWithIntegers(integers);  // OK - Integer является супер-классом себе
}

5. Ограничения для методов

public class GenericMethods {
    
    // Универсальный метод с границей
    public static <T extends Comparable<T>> T findMax(T... items) {
        T max = items[0];
        for (T item : items) {
            if (item.compareTo(max) > 0) {
                max = item;
            }
        }
        return max;
    }
    
    // Использование
    Integer maxInt = findMax(1, 5, 3, 2);  // 5
    String maxStr = findMax("apple", "zebra", "banana");  // "zebra"
    // Double maxDouble = findMax(1.5, 2.5);  // ОШИБКА - Double не Comparable<Double>
}

6. Практический пример: компаратор

public class Sorter {
    
    // Сортирует список элементов, реализующих Comparable
    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        java.util.Collections.sort(list);
    }
    
    // Сортирует список с использованием компаратора
    public static <T> void sort(List<T> list, Comparator<? super T> comp) {
        java.util.Collections.sort(list, comp);
    }
}

// Использование
List<String> strings = Arrays.asList("c", "a", "b");
Sorter.sort(strings);
System.out.println(strings);  // [a, b, c]

7. Рекурсивные ограничения типов

// Класс должен быть подклассом самого себя (для fluent API)
public abstract class Node<T extends Node<T>> {
    protected T next;
    
    public T setNext(T next) {
        this.next = next;
        return (T) this;
    }
    
    public abstract T self();
}

public class IntNode extends Node<IntNode> {
    private int value;
    
    public IntNode(int value) {
        this.value = value;
    }
    
    @Override
    public IntNode self() {
        return this;
    }
    
    public IntNode add(int val) {
        this.value += val;
        return this;
    }
}

// Использование
IntNode node = new IntNode(10)
    .add(5)
    .setNext(new IntNode(20))
    .setNext(new IntNode(30));

8. Таблица: когда использовать какое ограничение

ОграничениеСинтаксисКогда использоватьПример
Верхняя граница<T extends A>Нужны методы из A<T extends Number>
Множественные<T extends A & B>Нужны методы из A и B<T extends Number & Comparable>
Wildcard верхняя<? extends A>Чтение из коллекцииList<? extends Number>
Wildcard нижняя<? super A>Запись в коллекциюList<? super Integer>
Без границ<T>Полная гибкость<T> для параметра

9. Правило PECS

PECS = Producer Extends, Consumer Super

public class PECSExample {
    
    // Producer - расширяем (читаем данные)
    public static double calculateSum(List<? extends Number> producer) {
        double sum = 0;
        for (Number n : producer) {
            sum += n.doubleValue();  // Читаем
        }
        return sum;
    }
    
    // Consumer - суперклассируем (пишем данные)
    public static void addIntegers(List<? super Integer> consumer) {
        consumer.add(1);
        consumer.add(2);  // Пишем
    }
}

10. Типичные ошибки

// ❌ НЕПРАВИЛЬНО - нельзя использовать примитивные типы
// public class Box<T extends int> { }  // ОШИБКА!

// ✅ ПРАВИЛЬНО - используем Integer
public class Box<T extends Integer> { }

// ❌ НЕПРАВИЛЬНО - нельзя создавать инстансы с ?
// List<? extends Number> list = new ArrayList<?>();  // ОШИБКА!

// ✅ ПРАВИЛЬНО
List<? extends Number> list = new ArrayList<Integer>();

// ❌ НЕПРАВИЛЬНО - нельзя добавлять в wildcard extends
List<? extends Number> numbers = new ArrayList<Integer>();
// numbers.add(1);  // ОШИБКА! - компилятор не знает, Integer ли там
number = numbers.get(0);  // OK - читаем Number

Вывод

Ограничения типов — это мощный механизм для написания типобезопасного, переиспользуемого и гибкого кода. Правильное их применение предотвращает ошибки на этапе компиляции и делает API более понятным и удобным.

Как ограничить параметризованный тип данных | PrepBro