Wildcard что это в программировании

Wildcards

Consider the problem of writing a routine that prints out all the elements in a collection. Here’s how you might write it in an older version of the language (that is, a pre-5.0 release):

And here is a naive attempt at writing it using generics (and the new for loop syntax):

So what is the supertype of all kinds of collections? It’s written Collection (pronounced «collection of unknown»), that is, a collection whose element type matches anything. It’s called a wildcard type for obvious reasons. We can write:

Bounded Wildcards

Consider a simple drawing application that can draw shapes such as rectangles and circles. To represent these shapes within the program, you could define a class hierarchy such as this:

These classes can be drawn on a canvas:

Any drawing will typically contain a number of shapes. Assuming that they are represented as a list, it would be convenient to have a method in Canvas that draws them all:

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method. For instance, this is not allowed:

Bounded wildcards are just what one needs to handle the example of the DMV passing its data to the census bureau. Our example assumes that the data is represented by mapping from names (represented as strings) to people (represented by reference types such as Person or its subtypes, such as Driver ). Map is an example of a generic type that takes two type arguments, representing the keys and values of the map.

Again, note the naming convention for formal type parameters— K for keys and V for values.

Источник

Дженерики (Java, обучающая статья)

Предисловие

За основу данной статьи была взята информация из 6-ой главы книги «Oracle Certified Professional Java SE 7 Programmers Exams 1Z0-804 and 1Z0-805». Она была немного изменена (кое-где обрезана, а кое-где дополнена с помощью Google и Википедии). Здесь показаны далеко не все нюансы дженериков — для более подробной информации следует обратиться к официальной документации. Приятного прочтения.

Введение

Обобщённое программирование — это такой подход к описанию данных и алгоритмов, который позволяет их использовать с различными типами данных без изменения их описания. В Java, начиная с версии J2SE 5.0, добавлены средства обобщённого программирования, синтаксически основанные на C++. Ниже будут рассматриваться generics (дженерики) или > — подмножество обобщённого программирования.

Допустим мы ничего не знаем о дженериках и нам необходимо реализовать специфический вывод на консоль информации об объектах различного типа (с использованием фигурных скобок).

Ниже пример реализации:

В вышеприведённом коде была допущена ошибка, из-за которой на консоли мы увидим следующее:

Теперь на время забудем об этом примере и попробуем реализовать тот же функционал с использованием дженериков (и повторим ту же ошибку):

Самое существенное отличие (для меня) в том, что при ошибке, аналогичной предыдущей, проблемный код не скомпилируется:

Думаю, многие согласятся, что ошибка компиляции «лучше» ошибки времени выполнения, т.к. чисто теоретически скомпилированный код с ошибкой может попасть туда, куда ему лучше бы и не попадать. Это очевидное достоинство дженериков. Теперь подробнее рассмотрим конструкции, относящиеся к дженерикам в этом примере. Для того, чтобы код скомпилировался, достаточно заменить строку

Посмотрим на декларацию BoxPrinter:

После имени класса в угловых скобках » » указано имя типа «Т», которое может использоваться внутри класса. Фактически Т – это тип, который должен быть определён позже (при создании объекта класса).

Внутри класса первое использование T в объявлении поля:

Здесь объявляется переменная дженерик-типа (generic type), т.о. её тип будет указан позже, при создании объекта класса BoxPrinter.

В main()-методе происходит следующее объявление:

Здесь указывается, что Т имеет тип Integer. Грубо говоря, для объекта value1 все поля Т-типа его класса BoxPrinter становятся полями типа Integer (private Integer val;).
Ещё одно место, где используется T:

Как и в декларации val с типом Т, вы говорите, что аргумент для конструктора BoxPrinter имеет тип T. Позже в main()-методе, когда будет вызван конструктор в new, указывается, что Т имеет тип Integer:

Теперь, внутри конструктора BoxPrinter, arg и val должны быть одного типа, так как оба имеют тип T. Например следующее изменение конструктора:

приведёт к ошибке компиляции.

Последнее место использования Т в классе – метод getValue():

Тут вроде тоже всё ясно – этот метод для соответствующего объекта будет возвращать значение того типа, который будет задан при его (объекта) создании.

При создании дженерик-классов мы не ограничены одним лишь типом (Т) – их может быть несколько:

Нет ограничений и на количество переменных с использующих такой тип:

Алмазный синтаксис (Diamond syntax)

Вернёмся немного назад к примеру со строкой кода:

Если типы не будут совпадать:

То мы получим ошибку при компиляции:

Немного лениво каждый раз заполнять типы и при этом можно ошибиться. Чтобы упростить жизнь программистам в Java 7 был введён алмазный синтаксис (diamond syntax), в котором можно опустить параметры типа. Т.е. можно предоставить компилятору определение типов при создании объекта. Вид упрощённого объявления:

Следует обратить внимание, что возможны ошибки связанные с отсутствием «<>» при использовании алмазного синтаксиса

В случае с примером кода выше мы просто получим предупреждение от компилятора, Поскольку Pair является дженерик-типом и были забыты «<>» или явное задание параметров, компилятор рассматривает его в качестве простого типа (raw type) с Pair принимающим два параметра типа объекта. Хотя такое поведение не вызывает никаких проблем в данном сегменте кода, это может привести к ошибке. Здесь необходимо пояснение понятия простого типа.

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

Теперь посмотрим на вот этот:

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

Для простого типа получим ошибку времени выполнения (java.lang.ClassCastException), а для второго – ошибку компиляции. В общем, это очень похоже на 2 самых первых примера. Если в двух словах, то при использовании простых типов, вы теряете преимущество безопасности типов, предоставляемое дженериками.

Универсальные методы (Generic methods)

По аналогии с универсальными классами (дженерик-классами), можно создавать универсальные методы (дженерик-методы), то есть методы, которые принимают общие типы параметров. Универсальные методы не надо путать с методами в дженерик-классе. Универсальные методы удобны, когда одна и та же функциональность должна применяться к различным типам. (Например, есть многочисленные общие методы в классе java.util.Collections.)

Рассмотрим реализацию такого метода:

Нам в первую очередь интересно это:

» » размещено после ключевых слов «public» и «static», а затем следуют тип возвращаемого значения, имя метода и его параметры. Такое объявление отлично от объявления универсальных классов, где универсальный параметр указывается после имени класса. Тело метода вполне обычное – в цикле все элементы списка устанавливаются в одно значение (val). Ну и в main()-методе происходит вызов нашего универсального метода:

Стоит обратить внимание на то, что здесь не задан явно тип параметра. Для IntList – это Integer и 100 тоже упаковывается в Integer. Компилятор ставит в соответствие типу Т – Integer.

А сейчас вопрос – какая (-ие) из нижеприведённых строк откомпилируется без проблем?

Ответ с пояснением:
Первый вариант неправильный, т.к. нельзя создавать объект интерфейса.
Во втором случае мы создаем объект типа ArrayList и ссылку на него базового для ArrayList класса. И там, и там дженерик-тип одинаковый – всё правильно.
В третьем и четвёртом случае будет иметь ошибка компиляции, т.к. дженерик-типы должны быть одинаковыми (связи наследования здесь никак не учитываются).

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

Будем думать от обратного – допустим 3-ий вариант возможен. Рассмотрим такой код:

Wildcards (Маски)

Сейчас будут рассмотрены Wildcard Parameters (wildcards). Этот термин в разных источниках переводится по-разному: метасимвольные аргументы, подстановочные символы, групповые символы, шаблоны, маски и т.д. В данной статье я буду использовать «маску», просто потому, что в ней меньше букв…

Как было написано выше вот такая строка кода не скомпилируется:

Но есть возможность похожей реализации:

Под маской мы будем понимать вот эту штуку – » «.

А сейчас пример кода использующего маску и пригодного к компиляции:

Метод printList принимает список, для которого в сигнатуре использована маска:

И этот метод работает для списков с различными типами данных (в примере Integer и String).

Однако вот это не скомпилируется:

И ещё один маленький пример:

Тут не возникнет проблем компиляции. Однако нехорошо, что переменная numList хранит список со строками. Допустим нам нужно так объявить эту переменную, чтобы она хранила только списки чисел. Решение есть:

Данный код не скомпилируется, а всё из-за того, что с помощью маски мы задали ограничение. Переменная numList может хранить ссылку только на список, содержащий элементы унаследованные от Number, а всё из-за объявления: List numList. Тут мы видим, как маске задаётся ограничение – теперь numList предназначен для списка с ограниченным количеством типов. Double как и Integer наследуется от Number, поэтому код приведённый ниже скомпилируется.

То, что было описано выше называется ограниченными масками (Bounded wildcards). Применение таких конструкций может быть весьма красивым и полезным. Допустим нам необходимо посчитать сумму чисел различного типа, которые хранятся в одном списке:

Double-тип был использован для переменной result т.к. он без проблем взаимодействует с другими числовыми типами (т.е. не будет проблем с приведением типов).

На этом все. Надеюсь, данная статья была полезной.

Если Вам понравилась статья, проголосуйте за нее

Голосов: 175 Голосовать Wildcard что это в программировании. Смотреть фото Wildcard что это в программировании. Смотреть картинку Wildcard что это в программировании. Картинка про Wildcard что это в программировании. Фото Wildcard что это в программировании

Источник

— Ну, и напоследок еще одна маленькая лекция по Generic.

Сейчас я тебе расскажу, как обходить «стирание типов» (Type erasure).

— Ага. Мне тоже хочется это знать.

— Как ты уже наверное знаешь, в Java есть тип Class, который используется, чтобы хранить ссылку на объект класса. Примеры:

Но вот чего ты, наверное, не знаешь, так это того, что есть еще один класс Class, который является Generic’ом. И переменные этого Generic Class’а могут хранить только ссылки на тип, который был типом-параметром. Примеры:

— А почему оно так работает?

Но давай пойдем дальше.

Так вот, пользуясь тем фактом, что Class — это Generic, и тем, что переменная его типа может хранить значение только типа T, можно сделать вот такую хитрую комбинацию:

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

— Вот, слышу слова «не мальчика, но мужа!» Работает и ладно – это часто самый оптимальный вариант.

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

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

— А я сделаю свою Java с блекджеком и …

— Ладно, я уже подустал за день. Давай до свидания.

— До свидания, Риша, и спасибо за такой интересный урок.

Источник

SQL – Операторы Wildcards (Подстановочные операторы)

Главное меню » Базы данных » Учебное пособие по SQL » SQL – Операторы Wildcards (Подстановочные операторы)

Wildcard что это в программировании. Смотреть фото Wildcard что это в программировании. Смотреть картинку Wildcard что это в программировании. Картинка про Wildcard что это в программировании. Фото Wildcard что это в программировании

SQL поддерживает два подстановочных оператора в сочетании с оператором LIKE, которые подробно описаны в следующей таблице.

Sr.No.Подстановочные операторы & Описание
1Знак процента (%)

Соответствует одному или более символов.

Примечание – MS Access использует символ звездочки (*) символ подстановки вместо знака процента (%) подстановочный знак.

2Подчеркивание (_)

Соответствует одному символу.

Знак процента представляет ноль, один или несколько символов. Подчеркивание представляет собой одно целое число или символ. Эти символы могут быть использованы в комбинации.

Синтаксис

Основной синтаксис «%» и «_» оператора заключается в следующем.

Вы можете объединить N число условий с помощью операторов AND или OR. Здесь XXXX может быть любым числовым или строковым значением.

Примеры

В следующей таблице имеется ряд примеров, показывающих, где часть, имеющую различные положения, как и с операторами «_» «%» и.

Sr.No.Заявление и описание
1WHERE SALARY LIKE ‘200%’

Находит все значения, которые начинаются с 200.

2WHERE SALARY LIKE ‘%200%’

Находит все значения, которые имеют 200 в любом положении.

3WHERE SALARY LIKE ‘_00%’

Находит все значения, которые имеют 00 во второй и третьей позиции.

4WHERE SALARY LIKE ‘_00%’

Находит все значения, которые начинаются с 2, и по меньшей мере, 3-х символов в длину.

5WHERE SALARY LIKE ‘2_%_%’

Находит любые значения, которые заканчиваются 2.

6WHERE SALARY LIKE ‘%2’

Находит любые значения, которые имеют 2 во втором положении и в конце с 3.

7WHERE SALARY LIKE ‘_2%3’

Находит любые значения в пять-значного числа, которые начинаются с 2 и заканчивается 3.

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

Следующий блок кода является примером, который отобразит все записи из таблицы CUSTOMERS, где SALARY начинается с 55.

Это даст следующий результат:

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Источник

Практика применения Wildcards в Java: от простых Generic типов до подстановочных символов

Подстановочные символы Wildcards сегодня используются в большей степени для разработки библиотек и иногда в создании бизнес-приложений. Действительно мощный инструмент зачастую вызывает затруднение даже у senior программистов. Эксперт в области тестирования ПО, тренер Luxoft Training Денис Цыганов рассказал, в чем суть использования Wildcards и Generic в Java.

В чем суть

После появления Collection API в близком к современному виде разработчики Sun Microsystems (в дальнейшем и Oracle) искали решение для упрощения проверки безопасности по типам (typesafe) и ситуаций «загрязнения кучи» (heap pollution). Так появились Generic (обобщения), сделавшие возможным обнаружение несоответствия типов на этапе компиляции. До реализации этого механизма разработчики «ловили» указанные ошибки при непосредственном запуске приложения. В итоге, проблемы иногда могли впервые проявляться спустя годы использования продукта.

Разработчики Java внедряли Generics в существующую экосистему. Это заставило Oracle обеспечить обратную совместимость. Был выбран такой вариант реализации «дженериков», при котором все проверки выполнялись на этапе компиляции, а после происходило стирание типов (type erasure). То есть исполняемый код (скомпилированный с type erasure) ничем не отличается от скомпилированного кода без Generics. Следовательно, существующий не требовал изменения или перекомпиляции.

Как следствие такого решения, generic типы — инвариантны. Даже если generic типы находятся в отношении наследования, производные от этих типов в отношении наследования не находятся.

К примеру, следующая конструкция вызовет ошибку компиляции:

ArrayList list = new ArrayList ();

Это не всегда удобно, так как разработчикам часто хочется иметь метод с параметром, содержащим generic тип, который является более общим по отношения к используемым generic типам. Ожидая, что метод “охватит” эти типы. Например:

void doSomethingWithList(List list)

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

Для начала попробуем обойтись без Wildcards. В решении данного вопроса разработчику не поможет стандартный для подобных случаев механизм перегрузки методов. Следуя этому подходу можно написать несколько методов:

void doSomethingWithList(List list)

void doSomethingWithList(List list)

void doSomethingWithList(List list)

К сожалению, такой код не скомпилируется благодаря тому же стиранию типов. Т.к. после все три метода имеют одинаковую сигнатуру:

void doSomethingWithList(List list)

Мы можем поиграть с названиями методов, но их тело, скорее всего, будет выглядеть абсолютно одинаково (или очень похоже), а это нарушает один из главных принципов разработки — DRY (Don’t Repeat Yourself).

Эту проблему можно решить с помощью Wildcards. Символ «?» может быть использован вместо Generic типа, обозначая любой тип. Также можно задать границы для семейства типов, определенных обобщенным классом, делая api понятным и изящным. Wildcards способен ограничивать тип вниз (extends) или вверх (super).

Важно понимать, что Wildcard тип мо жет иметь только ссылка, но не сам объект. То есть он может быть задан для локальной переменной, поля класса, параметра метода или типа возвращаемого значения, но не может быть использован в операторе new :

new ArrayList (); // не скомпилируется

Примеры объявления переменных c Wildcards:

Первая и вторая ссылки — ковариантны. Третья — контравариантна.

Что это означает?

Примечание 1: обозначенные ограничения относятся только к Generic типу. Так, например, следующий код скомпилируется без проблем:

List list = new ArrayList ();

Для краткости можно опускать generic тип при создании объекта:

List list = new ArrayList<>();

Эта возможность появилась в версии 1.7 и называется Diamonds (угловые скобки напоминают бриллиант).

Примечание 2: Массивы в отличии от generics ковариантны. Иными словами, если Объекты находятся в отношении наследования, то и их производные (массивы), находятся в том же отношении. Т.е. следующий код корректен:

Object[] array = new Integer[5];

Практика применения

Чтобы понять суть Generic разберем пример программирования до и после их появления. Мир без «генериков» выглядит так:

// Список объектов без ограничения по типу

//теперь он называется raw type List

List listOfStrings = new ArrayList();

//В него можно добавить элементы любого объектного типа

Для извлечения элемента нужно знать точный тип (для применения оператора Cast).

String string = (String) listOfStrings.get(0);

//есть шанс получить ClassCastException, если мы не угадали с типом

String secondString = (String) listOfStrings.get(1);

Эта ошибка проявится в среде выполнения (Runtime) только во время выполнения конкретной строки кода (если соответствующая ветка исполняется не часто, велик шанс эту ошибку не обнаружить на этапе разработки).

С появлением «генериков» код выглядит иначе. Ошибки проявляются намного раньше — во время разработки, а стоимость их исправления значительно ниже. Кроме того, при извлечении элемента из списка нет необходимости приводить тип, а также знать (читай — искать по коду) точный тип — он будет предложен IDE или упомянут компилятором.

Список элементов определенного типа — String в примере ниже.

List trueListOfStrings = new ArrayList<>();

//it accepts String parameter only

В случае использования несовместимого типа — получим ошибку компиляции.

String alwaysCorrectType = trueListOfStrings.get(0);

//Возможность получить ClassCastException пропадает вместе с Cast операцией.

Теперь рассмотрим задачу отбора животных из существующего списка по определенному условию (conditional copy). В этом примере мы уже будем применять Wildcards.

Заранее обозначим требования к нашему API:

Для начала определим типы животных:

boolean result = false;

//имитация некоторой логики

class Cat extends Animal <

//переопределяем логику isNeeded() для класса Cat

Подготовим несколько списков для тестирования:

List objects = new ArrayList<>();

List cats = new ArrayList<>();

Теперь попробуем решить задачу «в лоб»:

void conditionalCopy(List dst, List src) <

/* при такой сигнатуре метода нет возможности вызвать методы класса Animal, т.к. тип T в рамках метода не ограничен и может представлять любой тип */

Итак, у нас проблема с реализацией требования 3.

void conditionalCopy(List dst, List src) <

List list = src.stream()

Проблема решена, пробуем копировать:

conditionalCopy(animals, cats); //1

conditionalCopy(animals, animals); //2

conditionalCopy(objects, cats); //3

Строки 1 и 3 вызывают проблему компиляции. Мы помним, что «дженерики» и поэтому требование 4 не реализовано. Давайте решать эту проблему:

void conditionalCopy(List dst, List src) <

List list = src.stream()

conditionalCopy(animals, cats); //1

conditionalCopy(animals, animals); //2

conditionalCopy(objects, cats); //3

Самое время применить контравариантность:

void conditionalCopy(List dst, List src) <

List list = src.stream()

Последняя проверка подтверждает правильность решения. Все строки компилируются и отлично работают.

Теперь реализованы все требования.

Кстати, наш API не позволяет некорректные варианты копирования вроде таких:

conditionalCopy(cats, animals); //4

conditionalCopy(cats, objects); //5

Обе строки (4) и (5) вызывают ошибку компиляции.

Несмотря на кажущуюся простоту Wildcards стали камнем преткновения для многих программистов. Я думаю, что использование символа «?» в каком-то смысле запутывает разработчика, создавая ощущение, что объект готов работать с любым типом в любой момент. Однако это не так. «Любой» — это не «каждый» и, тем более, не «все сразу». В данном контексте правильно было бы сказать «какой-то конкретный, из допустимого множества типов». Для их правильного определения необходима практика. Однако для полного понимания разработчикам необходимо узнать основные механизмы стирания типов, правильно использовать ограничения extends и super, принцип PECS, raw type, проблему heap pollution, и механизм Generic в целом.

Источник

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

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