Разбор использования вопросительного знака в Java Generics

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

В параметрах типа обобщений в Java символ вопроса ? обозначает неизвестный тип или так называемый метасимвол подстановки. Он позволяет реализовать гибкость в управлении типами данных, когда не требуется указание точных типовых границ. Если нам нужно работать с типом T или его подтипами, мы используем ? extends T, что позволяет читать данные, но не разрешает добавление новых элементов. В свою очередь, ? super T применяется для вставки экземпляров типа T или его супертипов в коллекцию. Такое использование обеспечивает гибкость кода и смягчает догматичность типовых ограничений. Пример кода ниже демонстрирует использование ограниченного метасимвола подстановки:

Java
Скопировать код
public void sum(List<? extends Number> numbers) {
    double total = 0.0;
    for(Number n : numbers) {
        total += n.doubleValue(); // Да, складывать так просто!
    }
}
Кинга Идем в IT: пошаговый план для смены профессии

Два случая использования метасимвола подстановки: часто непонятные концепции

Применяем ? extends T

Выражение ? extends T становится вашим ключом в работе с классами, которые являются подтипами T. Это может служить направляющим компасом к тем методам, которые определены в T или его наследниках. Использование такого метасимвола подстановки дает вам гибкость в работе с различными типами данных. Кто бы отказался от такой универсальности? 😏

Java
Скопировать код
public void printFirst(List<? extends HasWord> list) {
    if (!list.isEmpty()) {
        HasWord first = list.get(0);
        System.out.println(first); // Раскройся, истинный HasWord!
    }
}

List<Sentence> sentences = ...;
printFirst(sentences); // Вау! Sentence реализует HasWord.

Празднуем с ? super T

Когда вам требуется добавить объекты типа T или его подтипов в коллекцию, используйте ? super T. Этот подход лежит в основе концепции PECS (Producer Extends, Consumer Super) и гарантирует, что ваш метод сможет обработать коллекции, вмещающие T или более распространенный тип. Такая щедрость превращает отдых в настоящий праздник 🎅:

Java
Скопировать код
public void fillWithDefaultWords(List<? super Sentence> list) {
    list.add(new Sentence("default")); // Sentence для всех, как в шоу Опры!
}

List<HasWord> words = ...;
fillWithDefaultWords(words); // HasWord – родитель Sentence, всё на месте!

Визуализация

Markdown
Скопировать код
// Ваши обобщения – как комната с дверью, пометка на которой обозначает T
Room<T> 🏠🚪

// Метасимвол подстановки допускает ЛЮБОЙ тип двери
Room<?> 🏠🎟️

Ключевая мысль: Метасимвол подстановки ? обозначает неизвестный тип. Это ваш всемогущий проходной билет, который подходит к любому типу!

Markdown
Скопировать код
| Обобщение          | Принимает Тип Двери     |
| ----------------- | -----------------       |
| Room<ConcreteDoor>| 🚪(Только конкретная)   |
| Room<?>           | 🚪🚪🚪(Любой тип!)       |

Ограниченные метасимволы подстановки: мастера всех дел

Ограниченные метасимволы подстановки ? extends T и ? super T позволяют выразить ваши намерения точнее и являются предпочтительными в сравнении с неограниченными метасимволами подстановки или "сырыми" типами. Это ваш прямой билет к лучшему дизайну и более безопасному коду. Например, алгоритмы сортировки могут использовать Comparator<? super T>:

Java
Скопировать код
Collections.sort(list, new Comparator<SomeClass>() {
    public int compare(SomeClass o1, SomeClass o2) {
        // Секреты сравнения под бронеплитой!
    }
});

Таким образом, компаратор будет работать с кодом класса SomeClass или его суперклассами.

Жизненные уроки по типам

  • Применять ? extends T для обеспечения безопасного чтения данных, опустив добавление элементов наугад.
  • Дать приоритет ? super T для вставки элементов, без нарушения контрактов исходного типа.
  • Нет необходимости в instanceof при использовании ограниченных метасимволов подстановки; полиморфизм становится нашим другом.

День из жизни обобщений

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

Java
Скопировать код
public <E> void processData(Collection<? extends E> data) {
    for (E element : data) {
        // обработка элемента как вам угодно
    }
}

Теперь ваша Collection может вместить любой подтип E. Победа достигнута!

Полезные материалы

  1. Метасимволы подстановки в Java (учебник Oracle) – авторитетный источник знаний о обобщениях и метасимволах подстановки от бренда Oracle.
  2. Когда использовать обобщения, а когда метасимволы подстановки? (Stack Overflow) – практичные советы и рекомендации от профессионального сообщества разработчиков.
  3. Обобщённые типы в Java (DigitalOcean) – обучающий материал по применению метасимволов подстановки в обобщениях.
  4. Обобщения в Java (Tutorialspoint) – глубокое погружение в возможности и механизмы работы с обобщениями в Java.
  5. DZone – разведка в мире обобщений и метасимволов подстановки в Java с новой перспективы.