Что такое wildcard java
Перейти к содержимому

Что такое wildcard java

  • автор:

Java generics. В чем разница между wildcard() и parameterized types()?

T — тип, который произволен и при стирании трансформируется в Object — суперкласс всех классов в Java.
? — по идее то же самое или нет?
Я не могу понять, зачем нужен метасимвол ? . В Шилдте приведен пример с суммированием: если использовать T , то нельзя сравнивать между собой Double и Integer , а если ? , то можно. До конца не могу понять разницу. А также есть ли разница, например, и ? Ведь если они были бы одинаковы, то смысл вводить их в язык.

Отслеживать
111 3 3 бронзовых знака
задан 20 мая 2020 в 22:48
Александр Скворцов Александр Скворцов
123 1 1 серебряный знак 6 6 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Документация

Переменная типа указывается в объявлении обобщенного класса, интерфейса, метода или конструктора. После этого ее можно использовать в качестве типа в теле этого класса, интерфейса, метода или конструктора. Переменная типа может называться как угодно ( T , X , MyAwesomeType ).

Аргументы типа используются при использовании типа. Например, при объявлении переменной или аргумента метода. Аргументы типа могут являться ссылочными типами или символами подстановки.

Можете почитать об этом в спецификации языка Java:

  • §4.4. Переменные типа.
  • §4.5.1. Аргументы типа и символы подстановки.

Пример

В Шилдте приведен пример с суммированием: если использовать T, то нельзя сравнивать между собой Double и Integer, а если ?, то можно

Давайте попробуем разобрать пример. Насколько я понимаю используется код вроде этого:

class Stats  < T[] nums; Stats(T[] o) < nums = o; >double average() < return Arrays.stream(nums).mapToDouble(n ->n.doubleValue()).sum() / nums.length; > boolean sameAvg(Stats ob) < return average() == ob.average(); >public static void main(String args[]) < Statsiob = new Stats<>(new Integer[]); Stats dob = new Stats<>(new Double[]); if (iob.sameAvg(dob)) < System.out.println("are the same."); >else < System.out.println("differ."); >> > 

Во-первых, ? нельзя указать в качестве переменной типа. Если объявить класс так:

class Stats  

, то возникнет ошибка компиляциии так как мы объявляем переменную не указывая ее названия.

Во-вторых, мы не можем использовать T для аргумента типа sameAvg . Если сделать так:

boolean sameAvg(Stats ob)

, то не скомпилируется следующая строка

if (iob.sameAvg(dob))  

Дело в том, что T — это один конкретный тип для всего класса. Для переменной iob это Integer , соответственно ее метод sameAvg принимает только Stats и не может принять Stats .

Нам же здесь нужно два типа. Один из них T , он используется внутри класса. Другой: для аргумента ob в методе, чтобы класс мог взаимодействовать с объектами Stats других типов. Называть этот второй тип нам никак не нужно поэтому вместо него можно указать символ подстановки

boolean sameAvg(Stats ob) < //любой Stats, не только Stats

Второй тип можно объявить и явно в качестве переменной типа для метода sameAvg :

 boolean sameAvg(Stats ob) < //в примере выше T - Integer, U - Double return average() == ob.average(); >

Повторюсь, здесь в этом нет необходимости, т.к. U не используется внутри метода. В других случаях может понадобится именно такое объявление, а не символ подстановки. Почитайте Java: bounded wildcards or bounded type parameter?

Использование wildcard imports в Java. Хорошо или плохо?

В этой заметке мы обсудим преимущества и недостатки использования импорта wildcard Import в Java.

23 июля 2022 · 4 минуты на чтение

Разработчики Java используют подстановочный знак ( * ) в операторах import для добавления всех классов из определенного пакета. Но в ходе ревью большинство из вас, возможно, просили убрать этот подстановочный знак импорта и добавить полное имя класса. Разберемся почему не стоит использовать знак подстановки?

Но прежде, давайте вспомним, что полное имя класса, помимо названия, содержит также пакет, например: java.util.List . Такая запись позволяет нам иметь классы с одинаковыми именами, которые находятся в разных пакетах.

package com.example; public class WithoutImport < public static void main(String[] args) < java.util.List myList = new java.util.ArrayList(); >>

Однако, полная запись весьма непрактична, поэтому в Java существует оператор импорта – import . Операторы импорта объявляют компилятору источник имен классов, статических переменных и статических имен методов, используемых в коде. Один раз написали полное имя класса, а дальше используем только название класса.

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

Однако, это тоже не является проблемой. Достаточно удерживая Ctrl (cmd) навести курсор на класс и вы увидите пакет, в котором этот класс размещается.

Таким образом проблемы "чистого кода" более не актуальны.

Настройки в Idea

Intellij Idea автоматически сворачивает импорты, когда полные импорты из какого-то пакета достигают заданного количества. Эта опция настраивается в раделе Preferences > Editor > Code Style > Java во вкладке imports.

Чтобы отключить автоматическое сворачивание импортов достаточно указать число 999. Вот и все, теперь Idea не будет автоматически сворачивать импорты.

Резюмирую

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

Также при обновлении библиотек вы можете столкнуться с проблемой конфликта имен. Шанс этой проблемы не велик, но и не нулевой.

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

Wildcard для спецификации подтипов — Java: Дженерики

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

public class Application  public static Double average(ListInteger> numbers)  // Double var sum = 0.0; for (var number : numbers)  sum += number; > return sum / numbers.size(); > public static void main(String[] args)  var numbers = List.of(1, 2, 3, 4, 5); System.out.println(average(numbers)); // => 3.0 > > 

Если сделать из метода average() дженерик, то он не сработает, так как тип T будет Object , для которого операция сложения не определена.

public static T> Double average(ListT> numbers)  var sum = 0.0; for (var number : numbers)  // The operator += is undefined for the argument type(s) double, T sum += number; > return sum / numbers.size(); > 

Для подобных задач в Java есть механизм Wildcard, с его помощью можно уточнить типы, с которыми работает дженерик. В нашем случае и Integer и Double являются подтипами Number , а значит мы можем написать так: List . В таком случае в метод попадут только числа, какими бы они не были, а типом параметра numbers станет List .

public static T> Double average(List extends Number> numbers)  var sum = 0.0; for (var number : numbers)  sum += number; > return sum / numbers.size(); > 

Обновленный код почти работает. Он все еще выдает ошибку The operator += is undefined for the argument type(s) double, Number, так как во время сложения получается что тип переменной sum это Double , а тип переменной number - Number . Эта задача решается за счет метода doubleValue() определенного у Number , который любое число преобразует в Double . Рабочий код:

public static T> Double average(List extends Number> numbers)  var sum = 0.0; for (var number : numbers)  sum += number.doubleValue(); > return sum / numbers.size(); > 

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

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Дженерики в Java для самых маленьких: синтаксис, границы и дикие карты

Разбираемся, зачем нужны дженерики и как добавить их в свой код.

Оля Ежак для Skillbox Media

Екатерина Степанова

Екатерина Степанова

Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.

У нас в парадной подъезде рядом с почтовыми ящиками стоит коробка. Предполагалось, что туда будут выбрасывать бумажный спам, который какие-то вредители упорно кладут в эти самые ящики. Но в коробке вместе с бумажками лежат пустые бутылки и банки, подозрительного вида пакеты, а в нынешних реалиях — ещё и использованные медицинские маски. Почему люди так делают? Потому что так можно.

Теперь представьте, что содержимое коробки вы отвозите на переработку, а перед этим каждый раз приходится отделять бумагу от прочего мусора. Не хотели бы вы заполучить такую коробку, которая не даст положить в себя что-то, кроме бумаги? Если ваш ответ «да» — вам понравятся дженерики (generics).

Содержание

  • Знакомимся с дженериками
  • Объявляем дженерик-классы и создаём их экземпляры
  • Объявляем и реализуем дженерик-интерфейсы
  • Пишем дженерик-методы
  • Ограничиваем дженерики сверху и снизу
  • Wildcards
  • Собираем понятия, связанные с дженериками

Знакомимся с дженериками

До появления дженериков программисты могли неявно предполагать, что какой-то класс, интерфейс или метод работает с элементами определённого типа.

Посмотрите на этот фрагмент кода:

Паша быстро нашёл истинных виновников и попросил их исправить заполнение списка. Но на будущее решил подстраховаться от подобных ситуаций и переписал метод с использованием дженериков. Вот так:

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

Объявляем дженерик-классы и создаём их экземпляры

Давайте запрограммируем ту самую коробку, о которой шла речь в начале статьи: создадим класс Box, который умеет работать только с элементами определённого типа. Пусть для простоты в этой коробке пока будет только один элемент:

Собираем понятия, связанные с дженериками

Мы не успели разобраться с более сложными вещами — например, с заменами аргументов типов в классах-наследниках, с переопределением дженерик-методов, не узнали об особенностях коллекций с wildcards.

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

Термин Расшифровка
Дженерик-типы (generic types) Дженерик-класс или дженерик-интерфейс с одним или несколькими параметрами в заголовке
Параметризованный тип (parameterized types) Вызов дженерик-типа. Для дженерик-типа List параметризованным типом будет, например, List
Параметр типа (type parameter) Используются при объявлении дженерик-типов. Для Box T — это параметр типа
Аргумент типа (type argument) Тип объекта, который может использоваться вместо параметра типа. Например, для Box Paper — это аргумент типа
Wildcard Обозначается символом «?» — неизвестный тип
Ограниченный wildcard (bounded wildcard) Wildcard, который ограничен сверху — или снизу —
Сырой тип (raw type) Имя дженерик-типа без аргументов типа. Для List сырой тип — это List

Ещё больше о дженериках, коллекциях и других элементах языка Java узнайте на нашем курсе «Профессия Java-разработчик». Научим программировать на самом востребованном языке и поможем устроиться на работу.

Переменные ссылочного типа хранят адрес ячейки в памяти, в которой лежит значение этой переменной.
В этом их ключевое отличие от примитивных типов, когда в переменной хранится само значение.
Все ссылочные типы в Java наследуются от типа Object.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *