если поле типизировано дженериком как в байт коде будет представлен этот тип java

Пришел, увидел, обобщил: погружаемся в Java Generics

Java Generics — это одно из самых значительных изменений за всю историю языка Java. «Дженерики», доступные с Java 5, сделали использование Java Collection Framework проще, удобнее и безопаснее. Ошибки, связанные с некорректным использованием типов, теперь обнаруживаются на этапе компиляции. Да и сам язык Java стал еще безопаснее. Несмотря на кажущуюся простоту обобщенных типов, многие разработчики сталкиваются с трудностями при их использовании. В этом посте я расскажу об особенностях работы с Java Generics, чтобы этих трудностей у вас было поменьше. Пригодится, если вы не гуру в дженериках, и поможет избежать много трудностей при погружении в тему.

если поле типизировано дженериком как в байт коде будет представлен этот тип java. картинка если поле типизировано дженериком как в байт коде будет представлен этот тип java. если поле типизировано дженериком как в байт коде будет представлен этот тип java фото. если поле типизировано дженериком как в байт коде будет представлен этот тип java видео. если поле типизировано дженериком как в байт коде будет представлен этот тип java смотреть картинку онлайн. смотреть картинку если поле типизировано дженериком как в байт коде будет представлен этот тип java.

Работа с коллекциями

Предположим, банку нужно подсчитать сумму сбережений на счетах клиентов. До появления «дженериков» метод вычисления суммы выглядел так:

С появлением Generics необходимость в проверке и приведении типа отпала:

Во второй строчке проверки необходимость тоже отпадала. Если потребуется, приведение типов ( casting ) будет сделано на этапе компиляции.

Принцип подстановки

Тип Подтип
Number Integer
List ArrayList
Collection List
Iterable Collection

Примеры отношения тип/подтип

Вот пример использования принципа подстановки в Java:

Ковариантность, контравариантность и инвариантность

Но если мы попытаемся изменить содержимое массива через переменную arr и запишем туда число 42, то получим ArrayStoreException на этапе выполнения программы, поскольку 42 является не строкой, а числом. В этом недостаток ковариантности массивов Java: мы не можем выполнить проверки на этапе компиляции, и что-то может сломаться уже в рантайме.

«Дженерики» инвариантны. Приведем пример:

Wildcards

Всегда ли Generics инварианты? Нет. Приведу примеры:

Это ковариантность. List — подтип List

если поле типизировано дженериком как в байт коде будет представлен этот тип java. картинка если поле типизировано дженериком как в байт коде будет представлен этот тип java. если поле типизировано дженериком как в байт коде будет представлен этот тип java фото. если поле типизировано дженериком как в байт коде будет представлен этот тип java видео. если поле типизировано дженериком как в байт коде будет представлен этот тип java смотреть картинку онлайн. смотреть картинку если поле типизировано дженериком как в байт коде будет представлен этот тип java. extends B — символ подстановки с указанием верхней границы
super B — символ подстановки с указанием нижней границы
где B — представляет собой границу

2. Почему нельзя получить элемент из списка ниже?

The Get and Put Principle или PECS (Producer Extends Consumer Super)

Особенность wildcard с верхней и нижней границей дает дополнительные фичи, связанные с безопасным использованием типов. Из одного типа переменных можно только читать, в другой — только вписывать (исключением является возможность записать null для extends и прочитать Object для super ). Чтобы было легче запомнить, когда какой wildcard использовать, существует принцип PECS — Producer Extends Consumer Super.

и Raw типы

Если мы опустим указание типа, например, как здесь:

Если мы попытаемся вызвать параметризованный метода у Raw типа, то компилятор выдаст нам предупреждение «Unchecked call». Если мы попытаемся выполнить присваивание ссылки на параметризованный тип Raw типу, то компилятор выдаст предупреждение «Unchecked assignment». Игнорирование этих предупреждений, как мы увидим позже, может привести к ошибкам во время выполнения нашего приложения.

Wildcard Capture

Попробуем теперь реализовать метод, выполняющий перестановку элементов списка в обратном порядке.

Более подробно о Wildcard Capture можно прочитать здесь и здесь.

Вывод

Переменные типа

Вот еще пример из класса Enum:

Multiple bounds (множественные ограничения)

Вывод

Переменная типа может быть ограничена только сверху одним или несколькими типами. В случае множественного ограничения левая граница (первое ограничение) используется в процессе затирания (Type Erasure).

Type Erasure

На скриншоте ниже два примера программы:
если поле типизировано дженериком как в байт коде будет представлен этот тип java. картинка если поле типизировано дженериком как в байт коде будет представлен этот тип java. если поле типизировано дженериком как в байт коде будет представлен этот тип java фото. если поле типизировано дженериком как в байт коде будет представлен этот тип java видео. если поле типизировано дженериком как в байт коде будет представлен этот тип java смотреть картинку онлайн. смотреть картинку если поле типизировано дженериком как в байт коде будет представлен этот тип java.

Разница между ними в том, что слева происходит compile-time error, а справа все компилируется без ошибок. Почему?

Reifiable типы

Почему информация об одних типах доступна, а о других нет? Дело в том, что из-за процесса затирания типов компилятором информация о некоторых типах может быть потеряна. Если она потерялась, то такой тип будет уже не reifiable. То есть она во время выполнения недоступна. Если доступна – соответственно, reifiable.

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

И еще одна задачка. Почему в примере ниже нельзя создать параметризованный Exception?

Каждое catch выражение в try-catch проверяет тип полученного исключения во время выполнения программы (что равносильно instanceof), соответственно, тип должен быть Reifiable. Поэтому Throwable и его подтипы не могут быть параметризованы.

Unchecked Warnings

Компиляция нашего приложения может выдать так называемый Unchecked Warning — предупреждение о том, что компилятор не смог корректно определить уровень безопасности использования наших типов. Это не ошибка, а предупреждение, так что его можно пропустить. Но желательно все-так исправить, чтобы избежать проблем в будущем.

Heap Pollution

Как мы упомянули ранее, присваивание ссылки на Raw тип переменной параметризованного типа, приводит к предупреждению «Unchecked assignment». Если мы проигнорируем его, то возможна ситуация под названием » Heap Pollution » (загрязнение кучи). Вот пример:

В строке (1) компилятор предупреждает об «Unchecked assignment».

Рассмотрим еще один пример:

Java разрешает выполнить присваивание в строке (1). Это необходимо для обеспечения обратной совместимости. Но если мы попытаемся выполнить метод add в строке (2), то получим предупреждение Unchecked call — компилятор предупреждает нас о возможной ошибке. В самом деле, мы же пытаемся в список строк добавить целое число.

Reflection

Хотя при компиляции параметризованные типы подвергаются процедуре стирания (type erasure), кое-какую информацию мы можем получить с помощью Reflection.

С появлением Generics класс java.lang.Class стал параметризованным. Рассмотрим вот этот код:

Вывод

Если информация о типе доступна во время выполнения программы, то такой тип называется Reifiable. К Reifiable типам относятся: примитивные типы, непараметризованные типы, параметризованные типы с неограниченным символом подстановки, Raw типы и массивы, элементы которых являются reifiable.

Игнорирование Unchecked Warnings может привести к «загрязнению кучи» и ошибкам во время выполнения программы.

Reflection не позволяет получить информацию о типе объекта, если он не Reifiable. Но Reflection позволяет получить информацию о типе возвращаемого методом значения, о типе аргументов метода и о типе полей класса.

Type Inference

Термин можно перевести как «Вывод типа». Это возможность компилятора определять (выводить) тип из контекста. Вот пример кода:

С появлением даймонд-оператора в Java 7 мы можем не указывать тип у ArrayList :

Предположим у нас есть вот такой класс, который описывает связный список:

Результат обобщенного метода List.nil() может быть выведен из правой части:

Механизм выбора типа компилятором показывает, что аргумент типа для вызова List.nil() действительно String — это работает в JDK 7, все хорошо.

Выглядит разумно, что компилятор также должен иметь возможность вывести тип, когда результат такого вызова обобщенного метода передается другому методу в качестве аргумента, например:

В JDK 7 мы получили бы compile-time error. А в JDK 8 скомпилируется. Это и есть первая часть JEP-101, его первая цель — вывод типа в позиции аргумента. Единственная возможность осуществить это в версиях до JDK 8 — использовать явный аргумент типа при вызове обобщенного метода:

Вторая часть JEP-101 говорит о том, что неплохо бы выводить тип в цепочке вызовов обобщенных методов, например:

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

После выхода JEP 101 на StackOverflow появилось множество вопросов по теме. Программисты спрашивают, почему код, который выполнялся на 7-й версии, на 8-й выполняется иначе – или вообще не компилируется? Вот пример такого кода:

Посмотрим на байт-код после компиляции на JDK1.8:

А теперь байт-код после компиляции на JDK1.7 – то есть на Java 7:

Чтобы избежать таких проблем, Oracle выпустил руководство по переходу с JDK1.7 на JDK 1.8 в котором описаны проблемы, которые могут возникнуть при переходе на новую версию Java, и то, как эти проблемы можно решить.

Например если вы хотите, чтобы в коде выше после компиляции на Java 8 все работало так же, как и на Java 7, сделайте приведение типа вручную:

Заключение

На этом мой рассказ о Java Generics подходит к концу. Вот другие источники, которые помогут вам в освоении темы:

Источник

Какова концепция стирания в генерики в Java?

какова концепция стирания в дженериках на Java?

8 ответов

это, конечно, делает больше, чем просто стирание; все автоматические вещи компилятор делается здесь. Например, также вставляются конструкторы по умолчанию, новый foreach-style for петли расширяются до регулярных for петли и т. д. Приятно видеть мелочи, которые происходит автоматически.

чтобы завершить уже очень полный ответ Джона Скита, вы должны реализовать концепцию стирания типа происходит от потребности совместимость с предыдущими версиями Java.

первоначально представленный на EclipseCon 2007 (больше не доступен), совместимость включала эти точки:

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

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

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

однако бывают случаи, когда одной статической проверки типа недостаточно, например:

обновление июля 2012 года, почти четыре года спустя:

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

чтобы облегчить взаимодействие с неродовым устаревшим кодом, также можно использовать стирание параметризованного типа в качестве типа. Такой тип называется raw тип (Спецификация Языка Java 3/4.8). Разрешение типа raw также обеспечивает обратную совместимость исходного кода.

в соответствии с этим, следующие версии java.util.Iterator класс как двоичный, так и исходный код обратно совместимы:

стирание, буквально означает, что информация о типе, присутствующая в исходном коде, стирается из скомпилированного байт-кода. Давайте разберемся в этом с помощью некоторого кода.

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

дополняя уже дополненный ответ Джона Скита.

было упомянуто, что реализация дженериков через стирание приводит к некоторым раздражающим ограничениям (например, нет new T[42] ). Было также упомянуто, что основной причиной для этого была обратная совместимость в байт-коде. Это также (в основном) верно. Сгенерированный байт-код-target 1.5 несколько отличается от просто de-sugared casting-target 1.4. Технически, это возможно (через огромный обман), чтобы получить доступ к экземплярам универсального типа во время, доказывая, что в байт-коде действительно что-то есть.

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

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

есть хорошие объяснения. Я только добавляю пример, чтобы показать, как стирание типа работает с декомпилятором.

декомпилированный код из байт-кода,

Generic programming вводится в версии java 1.5
прежде всего, что такое generic в java?
Generic programming-это объект типа safe, перед generic в коллекции мы можем хранить любой тип объекта. и после родовой мы должны хранить данные определенного типа объекта.

что преимущества родового?
Главные преимущества родового типа отливка необходимы и также тип-шалфей и родовое проверят время компиляции. и общий синтаксис Generic-ClassOrInterface здесь тип-это сигнал о том, что этот класс может установить класс при создании экземпляра

genericclassDemo = новый GenericClassDemo (сотрудник.java)

Источник

Стирание типов в Java Объяснено

Узнайте о важном механизме в том, как Java обрабатывает удаление общего типа.

1. Обзор

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

2. Что Такое Стирание Типа?

Удаление типов можно объяснить как процесс применения ограничений типа только во время компиляции и удаления информации о типе элемента во время выполнения.

Компилятор заменяет несвязанный тип E фактическим типом Объекта :

Поэтому компилятор обеспечивает типобезопасность нашего кода и предотвращает ошибки во время выполнения.

3. Типы стирания типов

Стирание типов может происходить на уровне классов (или переменных) и методов.

3.1. Стирание типа Класса

Давайте реализуем Стек с использованием массива:

При компиляции компилятор заменяет параметр несвязанного типа E на Объект :

В случае, когда параметр типа E привязан:

Компилятор заменит параметр связанного типа E первым связанным классом, Сопоставимым в этом случае :

3.2. Стирание Типа Метода

Давайте рассмотрим метод отображения содержимого любого заданного массива:

При компиляции компилятор заменяет параметр типа E на Объект :

Для параметра типа связанного метода:

У нас будет параметр типа E удален и заменен на Сопоставимый:

4. Крайние Случаи

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

Теперь давайте рассмотрим следующий код:

После стирания типа у нас есть:

Поэтому неудивительно, что попытка вставить строку и присвоить Целое число вызывает исключение ClassCastException из приведения, вставленного во время push компилятором.

4.1. Мостовые методы

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

В приведенном выше примере компилятор Java сохраняет полиморфизм универсальных типов после удаления, гарантируя отсутствие несоответствия сигнатур метода между Целым стеком s push(Целое число) методом и Стеком s push(объект) методом.

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

Следовательно, Стек метод класса push после удаления типа делегирует исходный push метод Целочисленного стека класса.

5. Заключение

В этом учебном пособии мы обсудили концепцию стирания типов с примерами в переменных и методах параметров типов.

Вы можете прочитать больше об этих концепциях:

Источник

Стирание типов дженериков в Java

Я понимаю, что параметр типа T стирается во время выполнения, но тогда почему параметр типа ob выжившие во время выполнения?

5 ответов

рассмотрим следующий пример:

но разве я не должен получить объект.toString ()?

стирания типа происходит. Generics-это система проверки типа времени компиляции. Во время выполнения вы все еще получаете класс (это информация о типе времени выполнения). Связанная документация по стиранию типа говорит (частично)

дженерики были введены в язык Java, чтобы обеспечить более жесткие проверки типов во время компиляции и обобщенного программирования. Для реализации дженериков компилятор Java применяет тип erasure к:

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

кроме того, о вашем вопросе о том, почему getClass() запоминает аргумент типа: это не так. Все, что он делает, это определяет класс контента. Для пример:

если теперь запустить следующий фрагмент.

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

помните, что выходит эффективно класс Gen

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

но после лучшего понимания Java generics я понял, что в моем вопросе я смешиваю 2 отдельные темы : Generics и Reflection.

возвращает время работы класс этого объекта.

и getClass() не возвращает тип ссылки, но фактический объект, на который ссылается ссылка.

Источник

Основы дженериков Java

Краткое введение в основы Java-дженериков.

1. введение

Дженерики Java были введены в JDK 5.0 с целью уменьшения ошибок и добавления дополнительного уровня абстракции над типами.

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

Дальнейшее чтение:

Ссылки на методы в Java

Получение полей из класса Java с помощью отражения

2. Потребность в дженериках

Давайте представим себе сценарий, в котором мы хотим создать список на Java для хранения Integer ; у нас может возникнуть соблазн написать:

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

Было бы намного проще, если бы программисты могли выразить свое намерение использовать определенные типы, а компилятор мог бы обеспечить правильность такого типа. Это основная идея, лежащая в основе дженериков.

Давайте изменим первую строку предыдущего фрагмента кода на:

Добавляя оператор diamond<>, содержащий тип, мы сужаем специализацию этого списка только до Integer type, т. е. указываем тип, который будет храниться внутри списка. Компилятор может принудительно применить тип во время компиляции.

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

3. Общие методы

Универсальные методы-это те методы, которые написаны с одним объявлением метода и могут вызываться с аргументами разных типов. Компилятор обеспечит правильность любого используемого типа. Вот некоторые свойства универсальных методов:

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

Мы передаем функцию, которая преобразует массив с элементами типа T в список с элементами типа G. Примером может быть преобразование Целого числа в его строковое представление:

Стоит отметить, что Oracle рекомендует использовать заглавную букву для представления общего типа и выбирать более описательную букву для представления формальных типов, например, в коллекциях Java T используется для типа, K для ключа, V для значения.

3.1. Ограниченные дженерики

Как упоминалось ранее, параметры типа могут быть ограничены. Ограниченный означает ” ограниченный “, мы можем ограничить типы, которые могут быть приняты методом.

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

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

Ключевое слово extends используется здесь для обозначения того, что тип T расширяет верхнюю границу в случае класса или реализует верхнюю границу в случае интерфейса.

3.2. Множественные границы

Тип также может иметь несколько верхних границ следующим образом:

4. Использование Подстановочных Знаков С Дженериками

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

Известно, что Объект является ли супертип всех классов Java, однако, коллекцией Объект не является супертипом какой-либо коллекции.

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

То же правило применяется к любой коллекции типа и его подтипов. Рассмотрим этот пример:

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

5. Тип Стирания

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

Это пример стирания типов:

При стирании типа неограниченный тип T заменяется на Object следующим образом:

Если тип ограничен, то во время компиляции он будет заменен на связанный:

изменится после компиляции:

6. Универсальные и примитивные типы данных

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

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

В качестве примера давайте рассмотрим метод add списка:

Подпись метода add является:

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

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

Итак, если мы хотим создать список, который может содержать целые числа, мы можем использовать оболочку:

Скомпилированный код будет эквивалентен:

7. Заключение

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

Источник

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

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