Что такое дженерики какую проблему они решают
Что такое дженерики?
Изобретение нового лекарства, процесс его испытания, клинические исследования и регистрация занимают много времени. Иногда с момента получения молекулы действующего вещества до появления в продаже первой упаковки может пройти 10-15 лет. Этот процесс экономически затратный, поэтому оригинальный препарат часто дорогой. Но позже в продаже появляются аналоги, которые часто стоят гораздо дешевле. Если в них используется то же самое действующее вещество, препарат называют дженериком.
Что такое препараты-дженерики
Generic drug, что значит дженерик – это лекарственный препарат, который в своей основе имеет тоже количество и качество активного вещества, что и в оригинальном средстве. Он должен выпускаться в той же лекарственной форме, что и эталон. Обязательное условие – эффективность дженерика.
После изобретения лекарства, компания получает патент на оригинальную молекулу действующего вещества. Исключительные права на производство и продажу сохраняются за компанией в течение 20 лет. После этого другие фармкомпании могут производить дженерики лекарственного средства. Например, существует оригинальное лекарство российского производства Арбидол. Но в 2007 г у него закончилось действие патента. Поэтому вскоре появились дженерики Арпетол (Беларусь), Иммуностат.
У них одинаковое международное непатентованное название (МНН), но производитель в праве изменять упаковку, давать собственное торговое название лечебному средству. Поэтому в продаже есть Ибупрофен и его дженерик Нурофен.
К дженерикам предъявляются не менее строгие требования к качеству и эффективности. Несмотря на использование одного и того же действующего вещества, влияние на организм может отличаться. Это связано с несколькими факторами:
Аналоги проходят большое количество испытаний. Фармацевтическая компания, которая производит дженерик, должна доказать, что его эффект эквивалентен оригинальному средству. В исследованиях обращают внимание на то, с какой скоростью всасывается лекарство, какой максимальной концентрации оно достигает в крови и как быстро метаболизируется.
К некоторым лекарственным средствам предъявляют требования, чтобы и терапевтический эффект соответствовал оригиналу. В этом случае не ограничиваются лабораторным подтверждением концентрации вещества в крови, а изучают реакцию пациентов.
Более мягкие требования к лекарственным средствам, которые не всасываются в кровь. К этой группе относятся кожные мази, глазные капли. В этом случае для фармакологической компании достаточно доказать, что состав препарата соответствует оригиналу.
Почему дженерики дешевле оригинальных препаратов?
Выбирая при покупке между дженериком и оригиналом, многие отдают предпочтение более дешевому лекарству-копии. Невысокая цена аналога объясняется тем, что компания не тратила деньги на его изобретение, проверку безопасности и эффективности – один из наиболее дорогих этапов производства лекарства. Ведь создание молекулы действующего вещества сопровождается неудачными вариантами.
В стоимость входят вспомогательные вещества. В фармакологии их назначение – придавать форму, связывать основной компонент.
Если производителю удастся дешево купить сырье, исключить или поменять компонент без потери свойств, то можно будет продавать лекарство по низкой цене. У потребителя появляется вариант: купить оригинальную Но-шпу или довериться аналогу Дротаверину.
Снизить стоимость нового лекарственного препарата помогает оптимизация расходов на налоги, уменьшение стоимости труда людей. Часто производство дженериков находится в Индии, Китае, где оплата труда рабочих ниже. Но благодаря высокотехнологичному производству, удается поддерживать высокое качество лекарственного средства.
Но стоимость дженерика не всегда ниже оригинала. Если производитель изначально для производства приобрел дорогое сырье, увеличил расходы на упаковку и маркетинг, то итоговая стоимость может превысить эталонный препарат. Например, таблетки для лечения диабета 2 типа Глюкофаж – это оригинал, а Сиофор – дженерик, который в одинаковой дозировке стоит больше эталона.
Почему дженерики могут быть менее эффективными?
У денерика лекарства действующее вещество препарата, и влияние на организм должно быть сопоставимо с оригиналом. Но некоторые врачи и пациенты утверждают, что у определенных лекарств он может существенно отличаться. Чаще всего на это обращают внимание при покупке антибиотиков. Оригинальный препарат Аугментин в аптеках продается как Амоксиклав и Фемоклав. По утверждению врачей копия в этом случае не хуже оригинала.
Но такое возможно не у всех лекарств. Снижение эффективности может наблюдаться в случаях, когда производитель использует другие вспомогательные вещества, которые отличаются по составу от оригинала. Обычно это инертные компоненты, которые не оказывают лечебного действия. Но к ним может возникать аллергия или индивидуальная непереносимость, которая не позволит пациенту использовать дженерик.
В фармакологии при испытаниях дженерика вспомогательные компоненты могут не проявить свое негативное действие, все эффекты препарата будут сохранены. Но позже при использовании лекарства на практике него будут более выражены побочные эффекты или он окажется недостаточно эффективным. Это может привести к повышению дозы препарата и увеличению риска нежелательных действий.
Стоит ли использовать дженерики
Препараты-дженерики занимают около 60% от общего рынка лекарств. Из-за стоимости и хорошей эффективности их приобретают разные группы покупателей лучше, чем оригиналы.
Для большинства заболеваний не имеет значение, какой тип препарата используют. Диклофенак также эффективно борется с болью и воспалением, как и Вольтарен. И никто не задумывается, какой из препаратов был изобретен первым.
Но в фармакологии есть лекарственные средства, к которым предъявляют повышенные требования. Это препараты для лечения онкологических больных на курсе химиотерапии, большая часть групп антибиотиков.
Чтобы выявить отличия в работе лекарственных препаратов, необходимо исследовать терапевтическую эквивалентность. Но пока этот этап не является обязательным.
В США в помощь врачам разработана «Оранжевая книга». Это пособие, на страницах которого собраны списки оригинальных препаратов. В следующих разделах книги можно найти дженерики необходимого препарата с доказанной эффективностью. Они формируют список А и расположены на зеленых страницах. Если название препарата обнаружено на красных страницах книги, это обозначает, что оно соответствует эффективности, но имеет низкий уровень доказательств.
Врачи сами иногда предпочитают дженерики, а не оригинальные препараты. Так вместо дорогого Мезима пациентам с нарушением пищеварения может помочь его аналог Панкреатин. Но такая рекомендация обычно основана на опыте использования определенного препарата и не может транслироваться автоматически на все средства. Если нет уверенности в эффективности, лучше доверить свое здоровье производителю оригинального лекарства.
Будик Александр Михайлович, невролог, КМН, главный врач медицинских кабинетов 36,6
ИМЕЮТСЯ ПРОТИВОПОКАЗАНИЯ, ПЕРЕД ПРИМЕНЕНИЕМ НЕОБХОДИМО ПРОКОНСУЛЬТИРОВАТЬСЯ СО СПЕЦИАЛИСТОМ
Что такое дженерики в Java?
Рассмотрим пример, в котором вы должны составить список живых существ в местности. Неважно, человек это, животное или растение. Все, что имеет значение, является живым существом. В этом случае вы бы сгруппировали их как «живые существа», а не классифицировали их.
Точно так же, когда вам нужно хранить некоторые данные, для вас важен контент, а не тип данных, и именно здесь вы используете дженерики. Обобщения в Java – это языковая функция, которая позволяет использовать универсальные типы и методы.
Что такое Generics в Java?
Дженерики в Java – это термин, обозначающий набор языковых возможностей, связанных с определением и использованием общих типов и методов. Общие методы Java отличаются от обычных типов данных и методов. До Generics мы использовали коллекцию для хранения любых типов объектов, т.е. неуниверсальных. Теперь Generics заставляет программиста Java хранить объекты определенного типа.
Если вы посмотрите на классы платформы Java-коллекции, то увидите, что большинство классов принимают параметр / аргумент типа Object. По сути, в этой форме они могут принимать любой тип Java в качестве аргумента и возвращать один и тот же объект или аргумент. Они в основном неоднородны, т.е. не похожего типа.
Иногда в приложении Java тип данных ввода не является фиксированным. Входными данными могут быть целое число, число с плавающей запятой или строка. Чтобы назначить ввод переменной правильного типа данных, необходимо было провести предварительные проверки.
В традиционном подходе после получения ввода проверяется тип данных ввода, а затем назначается переменная правого типа данных. При использовании этой логики длина кода и время выполнения были увеличены. Чтобы избежать этого, были введены дженерики.
Когда вы используете Generics, параметры в коде автоматически проверяются во время компиляции, и он устанавливает тип данных по умолчанию. Так что это то место, где вам нужна концепция обобщений в Java.
Существует 4 различных способа применения:
1. Типовой класс
Класс называется дженериком, если он объявляет одну или несколько переменных типа. Эти типы переменных известны как параметры типа класса Java. Давайте разберемся с этим на примере. В приведенном ниже примере я создам класс с одним свойством x, а типом свойства является объект.
Здесь, как только вы инициализируете класс с определенным типом, класс должен использоваться только с этим конкретным типом. Например, если вы хотите, чтобы один экземпляр класса содержал значение x типа ‘String’, программист должен установить и получить единственный тип String.
Поскольку я объявил тип свойства для объекта, нет никакого способа применить это ограничение. Программист может установить любой объект и может ожидать любой тип возвращаемого значения от метода get, поскольку все типы Java являются подтипами класса Object.
Чтобы применить этот тип ограничения, мы можем использовать обобщенные значения, как показано ниже:
Теперь вы можете быть уверены, что класс не будет неправильно использоваться с неправильными типами. Простой пример «Genericclass» выглядит так, как показано ниже:
Эта аналогия верна и для интерфейса.
2. Интерфейс
Интерфейс в Java относится к абстрактным типам данных. Они позволяют манипулировать коллекциями Java независимо от деталей их представления.
Кроме того, они образуют иерархию в объектно-ориентированных языках программирования.
3. Методы
Универсальные методы очень похожи на универсальные классы. Они отличаются друг от друга только одним аспектом, заключающимся в том, что информация о области действия или типе находится только внутри метода. Универсальные методы вводят свои параметры типа.
Если вы передадите список String для поиска в этом методе, он будет работать нормально. Но если вы попытаетесь найти число в списке строк, это даст ошибку времени компиляции.
4. Конструктор
Конструктор Java – это блок кода, который инициализирует вновь созданный объект. Конструктор напоминает метод экземпляра в Java, но это не метод, поскольку он не имеет возвращаемого типа. Конструктор имеет то же имя, что и класс, и выглядит так в коде Java.
В приведенном выше примере конструктор класса Dimension содержит информацию о типе. Таким образом, вы можете иметь экземпляр измерения со всеми атрибутами только одного типа.
Преимущества дженериков в Java
1. Повторное использование кода.
Вы можете составить стратегию, класс или интерфейс один раз и использовать их для любого типа или любым другим способом.
2. Кастинг отдельных типов не требуется.
По сути, вы восстанавливаете информацию из ArrayList каждый раз, когда вам нужно ее типизировать.
Типирование при каждой задаче восстановления является серьезной задачей. Чтобы искоренить этот подход, были введены дженерики.
3. Реализация неуниверсального алгоритма.
Он может рассчитывать алгоритмы, которые работают с различными типами элементов, которые также являются безопасными типами.
Дженерики в Java для самых маленьких: синтаксис, границы и дикие карты
Разбираемся, зачем нужны дженерики и как добавить их в свой код.
Оля Ежак для Skillbox Media
У нас в парадной подъезде рядом с почтовыми ящиками стоит коробка. Предполагалось, что туда будут выбрасывать бумажный спам, который какие-то вредители упорно кладут в эти самые ящики. Но в коробке вместе с бумажками лежат пустые бутылки и банки, подозрительного вида пакеты, а в нынешних реалиях — ещё и использованные медицинские маски. Почему люди так делают? Потому что так можно.
Теперь представьте, что содержимое коробки вы отвозите на переработку, а перед этим каждый раз приходится отделять бумагу от прочего мусора. Не хотели бы вы заполучить такую коробку, которая не даст положить в себя что-то, кроме бумаги? Если ваш ответ «да» — вам понравятся дженерики (generics).
Содержание
Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.
Знакомимся с дженериками
До появления дженериков программисты могли неявно предполагать, что какой-то класс, интерфейс или метод работает с элементами определённого типа.
Посмотрите на этот фрагмент кода:
Здесь предполагается, что метод printSomething работает со списком строк. Мы можем догадаться об этом, потому что в цикле все элементы приводятся (преобразуются) к классу String, а потом ещё и метод length этого класса вызывается.
Но смотрите, что сделали программисты Саша и Маша, — они поленились заглянуть внутрь метода и положили в список: один — число, а вторая — экземпляр StringBuilder.
Вот только тестировщик назначил баг не кому-то из них, а Паше, который написал метод printSomething, — потому что ошибка произошла именно во время его выполнения.
Паша быстро нашёл истинных виновников и попросил их исправить заполнение списка. Но на будущее решил подстраховаться от подобных ситуаций и переписал метод с использованием дженериков. Вот так:
Теперь, если кто-то захочет положить в массив нестроковый элемент, ошибка станет заметной сразу — ещё на этапе компиляции.
Обратите внимание, что во второй версии Пашиного метода item не приводится насильно к типу String. Мы просто получаем в цикле очередной элемент списка, и компилятор соглашается, что это, очевидно, будет строка. Код стал менее громоздким, читать его стало проще.
Объявляем дженерик-классы и создаём их экземпляры
Давайте запрограммируем ту самую коробку, о которой шла речь в начале статьи: создадим класс Box, который умеет работать только с элементами определённого типа. Пусть для простоты в этой коробке пока будет только один элемент:
В классе два метода:
Во всех случаях, кроме заголовка класса, символ T пишется без угловых скобок, он обозначает один и тот же параметр типа.
Теперь создадим коробку для бумаги. Пусть за бумагу отвечает класс Paper, а значит, экземпляр правильной коробки создаётся вот так:
Это полный вариант записи, но можно и короче:
Так как слева мы уже показали компилятору, что нужна коробка именно для бумаги, справа можно опустить повторное упоминание Paper — компилятор «догадается» о нём сам.
Это «угадывание» называется type inference — выведение типа, а оператор « <>» — это diamond operator. Его так назвали из-за внешнего сходства с бриллиантом.
E — element, для элементов параметризованных коллекций;
K — key, для ключей map-структур;
V — value, для значений map-структур;
N — number, для чисел;
T — type, для обозначения типа параметра в произвольных классах;
S, U, V и так далее — применяются, когда в дженерик-классе несколько параметров.
Дженерик-классы хороши своей универсальностью: с классом Box теперь можно создать не только коробку для бумаги, но и, например, коробки для сбора пластика или стекла:
А можно пойти ещё дальше и создать дженерик-класс с двумя параметрами для коробки с двумя отсеками. Вот так:
Теперь легко запрограммировать коробку, в одном отсеке которой будет собираться пластик, а во втором — стекло:
Обратите внимание, что type inference и diamond operator позволяют нам опустить оба параметра в правой части.
Объявляем и реализуем дженерик-интерфейсы
Объявление дженерик- интерфейсов похоже на объявление дженерик-классов. Продолжим тему переработки и создадим интерфейс пункта переработки GarbageHandler сразу с двумя параметрами: тип мусора и способ переработки:
Реализовать (имплементить) этот интерфейс можно в обычном, не дженерик- классе:
Но можно пойти другим путём и сначала объявить дженерик-класс с двумя параметрами:
Или скомбинировать эти два способа и написать дженерик-класс только с одним параметром:
Дженерик-классы и дженерик-интерфейсы вместе называются дженерик-типами.
Можно создавать экземпляры дженерик-типов «без расшифровки», то есть никто не запретит вам объявить переменную типа Box — просто Box:
Для такого случая даже есть термин — raw type, то есть «сырой тип». Эту возможность оставили в языке для совместимости со старым кодом, который был написан до появления дженериков.
В новых программах так писать не рекомендуется. Да и зачем? Ведь при таком способе теряются все преимущества использования дженериков.
Пишем дженерик-методы
В примерах выше мы уже видели параметризованные методы в дженерик-классах и интерфейсах. Типизированными могут быть как параметры метода, так и возвращаемый тип.
До этого мы использовали в методах только те обозначения типов, которые объявлены в заголовке дженерик-класса или интерфейса, но это не обязательно. Предположим, у нашего пункта переработки есть ещё опция: сбор опасных отходов, которые сотрудники вывозят на утилизацию в другое место. Напишем метод для этого:
У метода transfer есть свой личный параметр для типа, который не обязан совпадать ни с типом T, ни с типом S. При первом упоминании новый параметр, как и в случае с заголовком класса или интерфейса, пишется в угловых скобках.
Дженерик-методы можно объявлять и в обычных (не дженерик) классах и интерфейсах. Наш класс для переработки мог быть выглядеть так:
Здесь дженерики используются только в методе.
Обратите внимание на синтаксис: параметры типов объявляются после модификатора доступа ( public), но перед возвращаемым типом ( void). Они перечисляются через запятую в общих угловых скобках.
Ограничиваем дженерики сверху и снизу
Давайте немного расширим наше представление о мусоре и введём для него дополнительное свойство — массу «типичного представителя», то есть массу одной пластиковой бутылки или листка бумаги, например.
Теперь попробуем использовать эту массу в методе уже знакомого класса Box:
И получим ошибку при компиляции: мы не рассказали компилятору, что T — это какой-то вид мусора. Исправим это с помощью так называемого upper bounding — ограничения сверху:
Теперь метод getItemWeight успешно скомпилируется.
Здесь T extends Garbage означает, что в качестве T можно подставить Garbage или любой класс-наследник Garbage. Из уже известных нам классов это могут быть, например, Paper или Plastic. Так как и у Garbage, и у всех его наследников есть метод getWeight, его можно вызывать в новой версии дженерик-класса Box.
Для одного класса или интерфейса можно добавить сразу несколько ограничений. Вспомним про интерфейс для пункта приёма мусора и введём класс для метода переработки — HandleMethod. Тогда GarbageHandler можно переписать так:
Wildcards
Термин wildcard пришёл в программирование из карточной игры. В покере, например, так называют карту, которая может сыграть вместо любой другой. Джокер — известный пример такой «дикой карты».
Wildcard нельзя подставлять везде, где до этого мы писали буквенные обозначения. Не получится, например, объявить класс Box или дженерик-метод, который принимает такой тип:
Wildcards удобно использовать для объявления переменных и параметров методов совместно с классами из Java Collection Framework — здесь собраны инструменты Java для работы с коллекциями. Если вы не очень хорошо знакомы с ними, освежите знания, прочитав эту статью.
В примере ниже мы можем подставить вместо «?» любой тип, в том числе Paper, поэтому строка успешно скомпилируется:
Wildcards можно применять для ограничений типов:
Это уже знакомое нам ограничение сверху, upper bounding, — вместо «?» допуст им Garbage или любой его класс-наследник, то есть Paper подходит.
Но можно ограничить тип и снизу. Это называется lower bounding и выглядит так:
Собираем понятия, связанные с дженериками
Мы не успели разобраться с более сложными вещами — например, с заменами аргументов типов в классах-наследниках, с переопределением дженерик-методов, не узнали об особенностях коллекций с 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.
Пришел, увидел, обобщил: погружаемся в 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
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На скриншоте ниже два примера программы: Разница между ними в том, что слева происходит 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 подходит к концу. Вот другие источники, которые помогут вам в освоении темы:
|