Для чего нужны дженерики java
Что такое дженерики в Java? Руководство для начинающих по изучению дженериков
Эта статья о дженериках в Java даст краткое представление о том, что такое дженерики java и их различные типы, а также примеры.
Автор оригинала: Neha.
Рассмотрим пример, в котором вам нужно составить список живых существ в определенной местности. Не имеет значения, человек это, животное или растение. Все, что имеет значение, – это живое существо. В этом случае вы бы сгруппировали их всех как “Живые существа” и не классифицировали их. Аналогично, когда вам нужно хранить какие-то данные, для вас важно содержимое, а не тип данных, и именно здесь вы используете универсальные методы. Универсальные языки в Java-это языковая функция, которая позволяет использовать универсальные типы и методы.
Что такое дженерики в Java?
Теперь, когда вы знаете, что такое дженерики в Java, давайте двинемся дальше и поймем, зачем вам нужны дженерики Java.
Почему дженерики Java?
Теперь, когда вы получили некоторое представление о универсальных кодах, давайте продвинемся вперед и рассмотрим различные способы применения универсальных кодов к исходному коду.
Типы обобщений Java
Существует 4 различных способа применения дженериков в Java, и они заключаются в следующем:
Теперь давайте разберемся, как обобщения могут быть применены к классу типов в глубине.
1. Класс универсального Типа
Класс считается универсальным, если он объявляет одну или несколько переменных типа. Эти типы переменных известны как параметры типа класса Java. Давайте разберемся в этом с помощью примера. В приведенном ниже примере я создам класс с одним свойством x и типом свойства является объект.
Для обеспечения соблюдения этого типа ограничений мы можем использовать универсальные методы, как показано ниже:
Теперь вы можете быть уверены, что класс не будет использоваться неправильно с неправильными типами. Простой пример “общего класса” выглядит так, как показано ниже:
Так вот как это работает. Эта аналогия справедлива и для интерфейса. Давайте быстро рассмотрим пример, чтобы понять, как информация о типах обобщений может использоваться в интерфейсах на java.
2. Универсальный Интерфейс
Интерфейс в Java относится к абстрактным типам данных. Они позволяют управлять коллекциями Java независимо от деталей их представления. Кроме того, они образуют иерархию в объектно-ориентированном программировании языках. Давайте разберемся, как универсальный тип может быть применен к интерфейсам в Java.
Я надеюсь, что вы смогли понять, как универсальные типы могут быть применены к классу типов и интерфейсам. Теперь давайте углубимся в эту статью и поймем, насколько она полезна для методов и конструкторов.
3. Общие Методы
Универсальные методы во многом похожи на универсальные классы. Они отличаются друг от друга только в одном аспекте: информация о области или типе находится только внутри метода. Универсальные методы вводят свои собственные параметры типа.
Давайте разберемся в этом на примере. Ниже приведен пример универсального метода, который можно использовать для поиска всех вхождений параметров типа в списке переменных.
Если вы передадите список строк для поиска в этом методе, он будет работать нормально. Но если вы попытаетесь найти число в списке строк, это приведет к ошибке во время компиляции.
Эта аналогия аналогична и конструктору. Давайте возьмем пример универсального конструктора и поймем, как он работает.
4. Универсальный Конструктор
Конструктор – это блок кода, который инициализирует вновь созданный объект. конструктор напоминает метод экземпляра в Java но это не метод, так как у него нет возвращаемого типа. Конструктор имеет то же имя, что и класс, и выглядит так в коде Java. Теперь давайте возьмем пример и поймем, как это работает.
В приведенном выше примере конструктор класса измерений содержит информацию о типе. Таким образом, у вас может быть экземпляр измерения со всеми атрибутами только одного типа. Итак, вот как вы можете использовать конструкторы универсальных типов. Я надеюсь, вы поняли типы дженериков в Java.
Теперь давайте двинемся дальше и рассмотрим преимущества дженериков в Java.
Преимущества дженериков в Java
1. Возможность повторного использования Кода
Вы можете создать стратегию, класс или интерфейс один раз и использовать их для любого типа или любым способом, который вам нужен.
2. Кастинг отдельных типов не требуется
В принципе, вы каждый раз восстанавливали информацию из ArrayList, вам нужно ее типизировать. Типизация при каждой задаче восстановления-это серьезная мигрень. Чтобы искоренить этот подход, были введены дженерики.
3. Реализация нестандартного алгоритма
Он может вычислять алгоритмы, которые работают с различными типами элементов, которые также являются типобезопасными.
Это было все о преимуществах дженериков Java. На этом мы подходим к концу этой статьи о дженериках в Java. Я надеюсь, что вы нашли его информативным и помогли вам в понимании универсалий Java.
У вас есть к нам вопрос? Пожалуйста, укажите это в разделе комментариев этой статьи, и мы свяжемся с вами как можно скорее.
Дженерики в Java — [Примеры кода]
Сегодня мы поговорим о Дженериках (Generics) в языке Java. Иногда их называют обобщениями. В статье я буду использовать все эти употребления, но означают они одно и тоже.
Java — строго типизированный язык программирования. Поэтому если мы задаем для переменной какой-то тип — то должны всегда присваивать этой переменной значение строгого типа.
Иногда есть необходимость не указывать тип переменной. Зачастую есть ситуации когда разработчики не могут даже точно знать тип переменной. Вот Вам реальный пример. В Java есть классы коллекций. Представьте что Вы являетесь разработчиком этих классов. Вы же не можете знать какой тип объекта будут использовать люди при работе с Вашим классом.

Можно сделать по хитрому. Просто объявить тип переменной как Object. Ведь мы знаем что в Java все классы — это наследники Object. Код будет работать.
НО! Такой подход — очень не безопасный. Ведь при попытке получить объект из коллекции можно получить ошибку ClassCastException.
Вот что выводит код выше:
Все работает хорошо пока мы не вызвали элемент и не присвоили его значение неверному типу. Именно поэтому строго типизированные языки программирования считаются более безопасными. Мы знаем заранее тип переменной. И исходя из этого знания можем соответственно оперировать переменной.
Так как нам быть с коллекцией выше? Нам нужно чтобы она принимала любой тип объекта и при этом избежать ClassCastException.
Дженерики как раз и созданы для ситуации выше. С помощью универсального параметра мы можем указать, что наш класс или метод будет работать с любым типом. Только этот тип нужно передать на этапе создания объекта. В примере выше мы создаем CollectionExample с типом String. Теперь код
больше не будет компилироваться. Ведь коллекция ожидает от нас только переменных с типом String. Мы вроде как позволяем нашему классу работать с любыми объектами. И в тот же момент защищаем его от неправильного использования.
С дженериками можно пойти еще дальше. Можно указать что обобщенный тип может быть не таким уж и обобщенным.
При использовании дженериков можно передавать сразу несколько обобщенных типов:
Также как и классы, обобщенными могут быть интерфейсы и методы. Принцип работы такой же самый. Вот пример кода интерфейса:
Еще пример с методом, который использует универсальный параметр:
Как видно из примеров выше, дженерики — штука очень мощная. В умелых руках конечно))
Это все, что я хотел сказать об обобщенных типах в языке Java. Делайте примеры, учите теорию и дженерики станут Вам отличным инструментом в разработке.
Дженерики (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 Голосовать
Дженерики — что это?
— И ещё одна крутая тема.
— Одни сюрпризы. Прямо не день, а день рождения.
— Сегодня я расскажу тебе, что такое Generics. “Дженерики” – это типы с параметром. В Java классы-контейнеры позволяют указывать тип их внутренних объектов.
— Когда мы объявляем generic-переменную, то мы указываем не один тип, а два: тип переменной и тип данных, которые она у себя хранит.
Хороший пример этого – ArrayList. Когда мы создаём новый объект/переменную типа ArrayList, нам удобно указать, значения какого типа будут храниться внутри этого списка.
— Звучит очень интересно. Особенно про любой тип.
— Это только кажется, что это хорошо. На самом деле, если в одном методе в ArrayList кладутся строки, а в другом мы работаем с его содержимым и ожидаем, что там будут только числа, программа упадет (закроется с ошибкой).
— Пока что мы не будем создавать свои классы с типами-параметрами, но будем использовать чужие.
— А в качестве типа-параметра можно поставить любой класс, даже тот, что напишу я?
— Да, любой тип, кроме примитивных типов. Все классы-параметры должны быть унаследованы от класса Object.
— То есть, я не могу написать ArrayList<int>?
— В общем-то, да, не можешь. Но для примитивных типов разработчики языка Java написали их непримитивные аналоги — классы, унаследованные от Object. Вот как это будет выглядеть:
| Примитивный тип | Класс | Список |
|---|---|---|
| int | Integer | ArrayList Integer > |
| double | Double | ArrayList Double > |
| boolean | Boolean | ArrayList Boolean > |
| char | Character | ArrayList Character > |
| byte | Byte | ArrayList Byte > |
— Примитивные типы и их классы-аналоги (классы-обёртки) можно спокойно присваивать друг другу:
— Отлично. Тогда я думаю, я буду почаще использовать ArrayList.
Пришел, увидел, обобщил: погружаемся в Java Generics
Java Generics — это одно из самых значительных изменений за всю историю языка Java. «Дженерики», доступные с Java 5, сделали использование Java Collection Framework проще, удобнее и безопаснее. Ошибки, связанные с некорректным использованием типов, теперь обнаруживаются на этапе компиляции. Да и сам язык Java стал еще безопаснее. Несмотря на кажущуюся простоту обобщенных типов, многие разработчики сталкиваются с трудностями при их использовании. В этом посте я расскажу об особенностях работы с Java Generics, чтобы этих трудностей у вас было поменьше. Пригодится, если вы не гуру в дженериках, и поможет избежать много трудностей при погружении в тему.
Работа с коллекциями
Предположим, банку нужно подсчитать сумму сбережений на счетах клиентов. До появления «дженериков» метод вычисления суммы выглядел так:
С появлением Generics необходимость в проверке и приведении типа отпала:
Во второй строчке проверки необходимость тоже отпадала. Если потребуется, приведение типов ( casting ) будет сделано на этапе компиляции.
Принцип подстановки
| Тип | Подтип |
| Number | Integer |
| List | ArrayList |
| Collection | List |
| Iterable | Collection |
Примеры отношения тип/подтип
Вот пример использования принципа подстановки в Java:
Ковариантность, контравариантность и инвариантность
Но если мы попытаемся изменить содержимое массива через переменную arr и запишем туда число 42, то получим ArrayStoreException на этапе выполнения программы, поскольку 42 является не строкой, а числом. В этом недостаток ковариантности массивов Java: мы не можем выполнить проверки на этапе компиляции, и что-то может сломаться уже в рантайме.
«Дженерики» инвариантны. Приведем пример:
Wildcards
Всегда ли Generics инварианты? Нет. Приведу примеры:
Это ковариантность. List — подтип List


