What is the concept of erasure in generics in Java?
Какова концепция стирания в generics на Java?
Какова концепция стирания в generics на Java?
Переведено автоматически
Ответ 1
По сути, это способ, которым дженерики реализуются в Java с помощью хитрости компилятора. Скомпилированный универсальный код на самом деле просто использует java.lang.Object везде, о чем вы говорите T (или какой-либо другой параметр типа) - и есть некоторые метаданные, которые сообщают компилятору, что это действительно универсальный тип.
Когда вы компилируете некоторый код для универсального типа или метода, компилятор определяет, что вы на самом деле имеете в виду (т. Е. Каков аргумент типа для T) и проверяет во время компиляции, что вы поступаете правильно, но генерируемый код снова просто говорит в терминах java.lang.Object - компилятор генерирует дополнительные приведения там, где это необходимо. Во время выполнения a List<String> и a List<Date> точно такие же; дополнительная информация о типе была удалена компилятором.
Сравните это, скажем, с C #, где информация сохраняется во время выполнения, позволяя коду содержать такие выражения, как typeof(T) что эквивалентно T.class - за исключением того, что последнее недопустимо. (Есть дополнительные различия между .NET generics и Java generics, имейте в виду.) Стирание типов является источником многих "странных" предупреждений / сообщений об ошибках при работе с Java generics.
Просто в качестве дополнительного примечания, это интересное упражнение, чтобы на самом деле увидеть, что делает компилятор, когда он выполняет стирание - это немного упрощает понимание всей концепции. Существует специальный флаг, который вы можете передать компилятору для вывода java-файлов, в которых были удалены дженерики и вставлены приведения. Пример:
javac -XD-printflat -d output_dir SomeFile.java
-printflat - это флаг, который передается компилятору, генерирующему файлы. (-XD Часть - это то, что говорит javac передать это исполняемому jar, который на самом деле выполняет компиляцию, а не просто javac, но я отвлекся ...) -d output_dir Необходимо, потому что компилятору нужно какое-то место для размещения новых файлов .java.
Это, конечно, делает больше, чем просто стирание; все автоматические действия, выполняемые компилятором, выполняются здесь. Например, также вставлены конструкторы по умолчанию, новые for циклы в стиле foreach расширены до обычных for циклов и т.д. Приятно видеть мелочи, которые происходят автоматически.
Ответ 3
Стирание буквально означает, что информация о типе, присутствующая в исходном коде, стирается из скомпилированного байт-кода. Давайте разберемся в этом с помощью некоторого кода.
Если вы скомпилируете этот код, а затем декомпилируете его с помощью Java-декомпилятора, вы получите что-то вроде этого. Обратите внимание, что декомпилированный код не содержит следов информации о типе, присутствующей в исходном коде.
Чтобы дополнить и без того очень полный ответ Джона Скита, вы должны осознать, что концепция стирания типов вытекает из необходимости совместимости с предыдущими версиями Java.
Первоначально представленная на EclipseCon 2007 (больше недоступна), совместимость включала эти пункты:
Совместимость с исходным кодом (приятно иметь ...)
Двоичная совместимость (должна быть!)
Совместимость с миграцией
Существующие программы должны продолжать работать
Существующие библиотеки должны уметь использовать универсальные типы
Должно быть!
Оригинальный ответ:
Следовательно:
newArrayList<String>() => newArrayList()
Существуют предложения для большей овеществления. Значение Reify означает "Рассматривать абстрактное понятие как реальное", где языковые конструкции должны быть концепциями, а не просто синтаксическим сахаром.
Я должен также упомянуть checkCollection метод Java 6, который возвращает динамически типизированное представление указанной коллекции. Любая попытка вставить элемент неправильного типа приведет к немедленному ClassCastException.
Механизм generics в языке обеспечивает проверку типов во время компиляции (статическую), но можно обойти этот механизм с помощью непроверенных приведений.
Обычно это не проблема, поскольку компилятор выдает предупреждения обо всех таких непроверенных операциях.
Однако бывают случаи, когда одной статической проверки типов недостаточно, например:
когда коллекция передается в стороннюю библиотеку, и крайне важно, чтобы код библиотеки не повредил коллекцию, вставив элемент неправильного типа.
программа завершается с ошибкой ClassCastException, указывающей на то, что неправильно типизированный элемент был помещен в параметризованную коллекцию. К сожалению, исключение может возникнуть в любое время после вставки ошибочного элемента, поэтому обычно оно предоставляет мало информации или вообще не предоставляет никакой информации о реальном источнике проблемы.
Обновление от июля 2012 года, почти четыре года спустя:
Язык программирования Java реализует generics с использованием erasure, который гарантирует, что устаревшие и универсальные версии обычно генерируют идентичные файлы классов, за исключением некоторой вспомогательной информации о типах. Двоичная совместимость не нарушается, потому что можно заменить файл устаревшего класса файлом универсального класса без изменения или перекомпиляции какого-либо клиентского кода.
Для облегчения взаимодействия с неродовым унаследованным кодом также возможно использовать стирание параметризованного типа в качестве типа. Такой тип называется необработанным типом (спецификация языка Java 3/4.8). Разрешение необработанного типа также обеспечивает обратную совместимость исходного кода.
В соответствии с этим, следующие версии java.util.Iterator класса обратно совместимы как с двоичным кодом, так и с исходным кодом:
Class java.util.Iterator as it is defined in Java SE version 1.4: