Бесплатный вебинар
«как найти любимую работу»
Подарки на 150 000 ₽ за участие
Живой эфир
Записи не будет!
00:00:00:00
дн.ч.мин.сек.

Разница между ? и Object в Java-генериках: HashMap

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

Подстановочный символ ? используется в дженериках для обозначения неизвестного типа, обеспечивая при этом гибкость кода, который работает с различными типами данных.

Java
Скопировать код
List<?> wildcardList; // Список, который может содержать объекты любого типа.

Object — это корневой класс в иерархии Java. Использование его в дженериках означает, что список может содержать объекты любого типа, но без преимуществ типовой безопасности, который дженерики предлагают.

Java
Скопировать код
List<Object> objectList; // Список объектов без указания их конкретного типа.

Шаблоны с подстановочным символом ценятся в тех случаях, когда нужно обеспечить типовую инвариантность, в то время как использование Object может быть менее выразительным при описании содержимого коллекции или цели операции.

Кинга Идем в IT: пошаговый план для смены профессии

Гибкость и типовая безопасность

Гибкость шаблонов

Символ ? даёт возможность работать с коллекцией без уточнения типа её элементов. Например, HashMap<String, ?> позволяет использовать значения различных типов.

Java
Скопировать код
Map<String, ?> map = new HashMap<>();
map.put("key1", "value1");      // Здесь добавляется строка...
map.put("key2", 10);            // А тут — целое число!

Такой подход особенно полезен, когда важны только ключи коллекции или значения имеют разные типы.

Подробнее об этом расскажет наш спикер на видео
skypro youtube speaker

Ограниченные шаблоны для типовой безопасности

Выражения ? extends T и ? super T позволяют задавать ограничения на типы в дженериках, усиливая при этом типовую безопасность. List<? extends InputStream> примет только InputStream и его подклассы.

Java
Скопировать код
List<? extends InputStream> streamList = new ArrayList<FileInputStream>();

В этом контексте операции со списком можно выполнять, зная, что он содержит только объекты InputStream.

Принцип Получения и Размещения

Используйте ? extends T для извлечения и ? super T для добавления элементов в коллекцию, сохраняя чёткость намерений и типовую безопасность.

Разница между ковариантностью массивов и инвариантностью дженериков

Ковариантность массивов

Массивы в Java являются ковариантными. Это означает, что можно присвоить массив подкласса массиву суперкласса, но это может привести к ошибкам во время выполнения программы.

Java
Скопировать код
String[] strings = new String[1];
Object[] objects = strings; // Компилировать можно, но это небезопасно.

Инвариантность дженериков

В отличие от массивов, дженерики инвариантны: List<String> не является подтипом List<Object>, и попытка подобного приведения типов приведёт к ошибке компиляции.

Когда следует избегать шаблонов?

Запись в коллекцию

Избегайте использования подстановочных символов, если вам нужно читать из коллекции и записывать в неё данные. Это может нарушить типовую безопасность.

Java
Скопировать код
List<Integer> list = new ArrayList<>();
list.add(10); // Понятно и безопасно.

Методы с конкретными типами

Для методов лучше использовать конкретные обобщённые типы для сохранения типовой безопасности.

Java
Скопировать код
public <T> void processItems(List<T> items) {
    // Обработка элементов указанного типа T.
}

Хранилище и Держатель: Каждому своё место

Хранилище для различных объектов

Collection<Object> предназначена для хранения объектов любых типов, однако вы не сможете присвоить ей коллекцию конкретного типа, например, Collection<String>.

Java
Скопировать код
Collection<Object> objects;
Collection<String> strings = new ArrayList<>();
// objects = strings; // Здесь возникнет ошибка компиляции.

Держатель любых коллекций

В отличие от Collection<Object>, в Collection<?> можно хранить коллекцию любого типа. Это демонстрирует применение полиморфизма.

Java
Скопировать код
Collection<?> holder;
Collection<String> strings = new ArrayList<>();
holder = strings; // Подходит для коллекций любого типа.

Расширяя возможности с ?

HashMap<?, ?> дает свободу использования ключей и значений любых типов, что очень удобно, когда конкретные типы неизвестны или могут быть разнообразными.

Java
Скопировать код
Map<?, ?> myMap = ... ; // Подходит для пар "ключ-значение" любых типов.

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

Представьте себе дженерики в Java как набор инструментов, среди которых есть инструменты с конкретными обозначениями и универсальные, помеченные знаком вопроса.

Markdown
Скопировать код
| Тип инструмента | Описание в дженериках     |
| --------------- | ------------------------- |
| С меткой        | Object                    |
| Универсальный   | ? (подстановочный символ) |

Object в дженериках — это инструмент с конкретным назначением, в то время как ? подходит для самых разнообразных задач.

Практическое применение ? и Object

Проектирование API для коллекций

Понимание различий между ? и Object важно для создания гибкого и адаптивного API.

Java
Скопировать код
public void printCollection(Collection<?> c) {
    for (Object item : c) {
        System.out.println(item); // Просто печатаем объекты.
    }
}

Использование в Stream API

При работе со стримами в Java использование подстановочных символов позволяет обрабатывать элементы независимо от их конкретных типов.

Java
Скопировать код
Stream<?> mixedStream = Stream.of(1, "two", 3.0);
mixedStream.map(Object::toString).forEach(System.out::println); // Обработка и вывод каждого элемента.

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

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что обозначает подстановочный символ `?` в дженериках Java?
1 / 5
Свежие материалы
Видео уроки по Java
6 сентября 2024
Видео уроки по C++
6 сентября 2024