Что такое стирание и сырые типы 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. Заключение

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

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

Источник

Core Java

Курс лекций. Лекция 6

Иван Пономарёв, КУРС/МФТИ

До появления дженериков

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

После появления дженериков

Определяем собственный параметризованный класс

Определение и использование

Generic methods

Другой пример

Промежуточные выводы

Использование параметризованных классов — простое (просто укажите параметры, List )

Использование параметризованных методов — ещё проще: type inference: Manager m = getRandomItem(…​);

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

Bounded types

Intersection types

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Реализация дженериков

Появились в Java 5

Стояла задача обратной совместимости

Generics — возможность языка, а не платформы

Type Erasure, будь он неладен!

Сырые типы

Generic Type (source)

Raw Type (compiled)

Ограниченные типы вместо Object

Generic Type (source)

Raw Type (compiled)

Вызовы методов

Source code

Compiled

Bridge methods для сохранения полиморфизма

Source code

Compiled

Итог: как это работает

Параметризованных классов в JVM нет, только обычные классы и методы.

Типовые параметры заменяются на Object или на свою границу.

Для сохранения полиморфизма добавляются связывающие методы (bridge methods).

Сведение типов добавляется по мере необходимости.

Никогда не употребляйте сырые типы

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

Java5 вышла в 2004 году.

Понимание дженериков в Джаве — это не про то, что с ними делать можно, а про то, что с ними делать нельзя.

Стирание типов → невозможность определить параметр типа в Runtime

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Стирание типов до Object → невозможность использовать примитивные типы в качестве параметров

Примитивы и дженерики

День сегодняшний: нужна производительность? — пишем специальные реализации.

В стандартной библиотеке:

В специализированных библиотеках вроде fastutil:

HashMap → Int2ObjectMap (ВНИМАНИЕ: реальная потребность в таких библиотеках возникает редко!!)

День завтрашний: Project Valhalla, specialized generics. Решит проблему раз и навсегда.

Нельзя инстанцировать типы-параметры

Решается с помощью метакласса и рефлексии (о которой речь впереди)

Тем более нельзя инстанцировать массив типа-параметра

Решается передачей параметра, например, в ArrayList:

Массивы и дженерики — лютые враги

Забьём значения кувалдой и устроим heap pollution

Varargs — всё равно массив…​

Тот же heap pollution, что и в прошлом примере:

Компилятор что-то предчувствует…​

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Чтобы успокоить компилятор, надо поставить аннотацию @SafeVarargs :

…​и компилятор успокоится.

Зачем?!

Всё потому, что иметь varargs с параметризованными типами удобно.

Collections.addAll(Collection c, T…​ elements)

EnumSet.of(E first, E…​ rest)

Если вести себя хорошо, можно ставить @SafeVarargs, и всё будет хорошо:

Не записывать ничего в элементы массива,

Не раздавать ссылку на массив параметров наружу.

Нельзя параметризовать

ловля исключений — это проверка их типов,

дальше сырых типов мы в рантайме проверить не можем.

Инстанцируется по месту, не может быть несколько классов, параметризованных по-разному.

Параметры типов нельзя использовать в статическом контексте

Нельзя реализовывать разные параметризации одного и того же интерфейса

Source code

Compiled

Ковариантность массивов vs инвариантность дженериков

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Реальная картина

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Как быть, если хочется такого?

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Так не получится…​

Wildcard Types

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

В общем, addAllTo реализовать не получится…​

В обратную сторону (контравариантные типы)

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Unbounded wildcard

Класть можем только null.

Мнемоническое правило

PECS

Producer Extends, Consumer Super

Правила использования wildcard-типов

Используются в аргументах методов и локальных переменных.

Невидимы пользователю API, не должны возвращаться.

Их цель — принимать те аргументы, которые надо принимать, и отвергать те аргументы, которые надо отвергать.

Должны быть используемы в API, иначе API будет слишком «жёстким» и непригодным для использования.

Wildcard Capture

Метод с type capture

Recursive Generics

Что почитать-посмотреть

J. Bloch, Effective Java, 3rd ed. Chapter 5 — Generics. Addison-Wesley, 2018

Источник

Дженерики в Java для тех, кто постарше: стирание типов, наследование и принцип PECS

Рассказываем, как в любой непонятной ситуации правильно сочетать дженерик-типы.

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

В предыдущей статье «Дженерики для самых маленьких» мы рассказали о том, что такое дженерики (generics), зачем они нужны и как создавать дженерик-типы и методы. Там же говорили про ограничения (boundings) и wildcards. Без этих основ вам будет сложно разобраться с тем, что написано дальше. Поэтому освежите знания, если это необходимо.

Из этой статьи вы узнаете:

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.

Почему ни один дженерик не доживает до выполнения программы

Воспользуемся примером из первой части рассказа о дженериках: там был класс Box — коробка для сбора мусора: можно было положить в неё или извлечь из неё только объект определённого типа:

Теперь создадим экземпляр такого класса и подставим вместо T конкретный тип: например, Paper — для коробки, в которую будем собирать бумагу:

Можно предположить, что теперь мы имеем дело с таким классом:

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

Компилятор не генерирует class-файл для каждого параметризованного типа. Он создаёт один class-файл для дженерик-типа.

Компилятор стирает информацию о типе, заменяя все параметры без ограничений ( unbounded) типом Object, а параметры с границами ( bounded) — на эти границы. Это называется type erasure.

Кроме стирания (иногда говорят «затирания») типов, компилятор может добавлять приведение (cast) к нужному типу и создавать переходные bridge-методы, чтобы сохранить полиморфизм в классах-наследниках.

Пример 1. Стирание типа для дженерика без границ

Все параметры типов заменяются на Object. Вот что получится для нашего класса-коробки:

Пример 2. Стирание типа для дженерика с границами

Объявим дженерик-интерфейс c ограничением сверху ( upper bounding):

Вот что от этого останется после компиляции:

Пример 3. Bridge-метод

Создадим класс-наследник коробки для бумаги и переопределим в нём метод putItem:

Этому классу не всё равно, какого типа объекты приходят к нему в putItem, — нужно, чтобы они были типа Paper. Поэтому компилятору придётся немного докрутить класс — добавить в него bridge-метод с приведением типа:

А вот ещё несколько примеров дженерик-типов и того, что от них останется после компиляции:

До компиляцииПосле компиляции
>Box
Box
List []List[]

Теперь, когда вы знаете про type erasure и его последствия, наверняка сможете ответить на вопрос, почему нельзя создать дженерик-Exception:

В каждом блоке try catch проверяется тип исключения, так как разные типы исключений могут обрабатываться по-разному. Для дженерик-исключения определить конкретный тип было бы невозможно, а потому компилятор даже не даст его создать. Это правило относится к классу Throwable и его наследникам.

Как создать наследника дженерик-класса

Пример 1. Класс-наследник — не дженерик.

Чтобы получить обычный, не дженерик-класс, мы должны вместо параметра T передать какой-то конкретный тип, что мы и сделали — передали Paper.

Пример 2. Класс-наследник и сам дженерик с тем же числом параметров.

Параметры у Box и SuperGenericBox не обязаны обозначаться буквой T (от type) — можно брать любую. В этом примере важно, чтобы буквы были одинаковые, иначе компилятор не разберётся.

Пример 3. Класс-наследник — дженерик с другим числом параметров.

Здесь уже не один, а два параметра. Один передадим родителю, а второй используем как-нибудь ещё — например, напишем метод newMethod с параметром этого нового типа.

У наследника класса-дженерика может быть сколько угодно параметров, включая ноль (когда это вовсе не дженерик). Главное — помнить про все параметры типа родительского класса и передать для каждого параметра конкретный тип или какой-то из параметров класса-наследника.

Что не так с дженерик-типами классов-наследников

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

Например, PaperBox — наследник Box, и пример ниже успешно компилируется:

В терминах объектно-ориентированного программирования это называют отношением is a (является): бумажная коробка — это коробка (является коробкой). Или говорят, что PaperBox — это подтип (subtype) Box. При этом Box — супертип PaperBox.

Теперь возьмём не простую коробку, а её дженерик-вариант ( Box ), в которую будем класть разные типы мусора: Paper, Glass и тому подобные типы — наследники Garbage:

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

Но что, если Box станет типом параметра метода? Сможем ли мы в этом случае передать другой дженерик-тип? Напишем простой пример:

И убедимся, что замена тут не пройдёт. Несмотря на то что Paper — подтип Garbage, Box

Дженерики инвариантны. Это означает, что, даже если A — подтип B, дженерик от A не является подтипом дженерика от B.

Для сравнения, массивы в Java ковариантны: если A — подтип B, A[] — подтип B[].

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Как переопределить метод с дженерик-типами

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

Переопределение будет правильным, если тип переопределённого метода — это подтип исходного метода. Например, так:

Добавим немного дженериков и применим то же правило:

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

Звучит сложно, так что лучше взглянем на код:

Правда, в обоих случаях компилятор покажет предупреждение о небезопасном использовании типов ( unchecked warning):

Note: GlassBox.java uses unchecked or unsafe operations.

Его можно понять: исходный метод требует, чтобы возвращался список объектов типа Garbage, а переопределённые хотят просто какой-то список. Там могут быть объекты типа Garbage, а могут и любые другие — вот компилятору и тревожно.

Зато если в исходном методе возвращаемый тип — с wildcard без ограничений, то при аналогичном переопределении предупреждений не будет:

При переопределении дженерик-методов с одинаковым числом параметров типа можно произвольно менять обозначения этих параметров:

В переопределённом методе параметр типа назван S, а не T, но переопределение остаётся корректным.

А вот ограничения для дженерика в переопределённом методе добавлять нельзя:

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

Зато можно из дженерик-метода сделать обычный метод:

Компилятор спокоен, потому что метод в классе Box станет именно таким после type erasure — параметр типа будет заменён на Object.

Переопределение дженерик-метода будет корректно, если:

Как wildcards с ограничениями «портят» коллекции и зачем нужен принцип PECS

Если нужно что-то сделать с коллекциями объектов нескольких подтипов, удобны wildcards с ограничениями.

Например: List означает, что список может состоять из объектов типа Paper и всех его подтипов, а в List могут быть объекты типа Paper и всех супертипов — например, Garbage или Object.

С wildcards и коллекциями есть маленькая проблема — коллекции вроде тех, что в примере выше, нельзя использовать на полную катушку: свободно читать из них и записывать новые данные. Чтобы запомнить это ограничение, даже придумали принцип — принцип PECS.

PECS — Producer Extends, Consumer Super. Его суть:

Получается, в коллекцию с extends нельзя добавлять, а из коллекции с super нельзя читать? Вроде бы всё понятно, но давайте проверим:

Попробуем положить сюда экземпляр Paper — наследника Garbage:

Получим ошибку компиляции. Ладно, тогда, может, хотя бы объект типа Garbage подойдёт?

И снова нет. Принцип PECS не соврал — объект в такой список добавить нельзя. Единственное исключение — null. Вот так можно:

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

Добавим туда один объект типа Paper:

И попробуем его же прочитать. Если верить PECS, у нас это не должно получиться:

Но компилятору всё нравится — никаких ошибок нет. Проблемы, впрочем, начнутся, когда мы захотим сохранить полученное значение в переменной типа Paper или типа, совместимого с ним:

Вторая часть принципа PECS означает, что из коллекций, ограниченных снизу, нельзя без явного приведения типа (cast) прочитать объекты граничного класса, да и всех его родителей тоже. Единственное, что доступно, — тип Object:

К сожалению, принцип PECS ничего не говорит о том, какие объекты можно читать из producer, а какие добавлять в customer. Мы не придумали своего принципа, но сделали табличку, чтобы собрать вместе все правила:

Тип ограниченияЧто можно читатьЧто можно записывать
extends SomeType>Объекты SomeType и всех его супертиповТолько null
super SomeType>Объекты типа ObjectОбъекты типа SomeType и всех его подтипов

И даже картинку нарисовали:

Что такое стирание и сырые типы java. Смотреть фото Что такое стирание и сырые типы java. Смотреть картинку Что такое стирание и сырые типы java. Картинка про Что такое стирание и сырые типы java. Фото Что такое стирание и сырые типы java

Теперь точно не запутаетесь 🙂

Ещё больше хитростей дженериков и других особенностей Java — на курсе «Профессия Java-разработчик». Научим программировать на самом востребованном языке и поможем устроиться на работу.

Источник

Что такое необработанный тип и почему мы не должны его использовать?

Вопросов:

Что такое сырой тип?

Спецификация языка Java определяет необработанный тип следующим образом:

JLS 4.8 Необработанные типы

Необработанный тип определяется как один из:

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

Тип массива, тип элемента которого является необработанным типом.

Вот пример для иллюстрации:

mt имеет необработанный тип (и генерирует предупреждение о компиляции) по первому пункту в приведенном выше определении; inn также имеет необработанный тип по третьему пункту.

mt1 и mt2 оба объявлены с фактическими параметрами типа, поэтому они не являются необработанными типами.

Что такого особенного в необработанных типах?

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

Приведенный выше код работает просто отлично, но предположим, что у вас также есть следующее:

Смотрите также

Чем необработанный тип отличается от использования в качестве параметров типа?

Ниже приводится цитата из Effective Java 2nd Edition, пункт 23. Не используйте необработанные типы в новом коде :

Смотрите также

Чем необработанный тип отличается от использования в качестве параметра типа?

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

Вернуться к JLS 4.8:

Суперклассы (соответственно, суперинтерфейсы) необработанного типа являются стиранием суперклассов (суперинтерфейсов) любой из параметризаций универсального типа.

Возьмите следующий пример:

JLS 4.6 продолжает объяснять следующее:

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

Удаление подписи универсального метода не имеет параметров типа.

Если это небезопасно, почему разрешено использовать необработанный тип?

Вот еще одна цитата из JLS 4.8:

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

Effective Java 2nd Edition также имеет следующее:

Учитывая, что вы не должны использовать необработанные типы, почему разработчики языка разрешили это? Для обеспечения совместимости.

Есть ли исключения?

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

Смотрите также

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

Хотя это работало большую часть времени, ошибки все же случались

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

Более сложный сравниваемый интерфейс:

Обратите внимание, что невозможно реализовать CompareAble интерфейс compareTo(MyCompareAble) с необработанными типами. Почему вы не должны использовать их:

Что делает компилятор: Generics обратно совместимы, они используют те же классы Java, что и необработанные типы. Магия происходит в основном во время компиляции.

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

Это тот же код, который вы написали бы, если бы использовали непосредственные типы. Хотя я не уверен, что происходит с CompareAble интерфейсом, я предполагаю, что он создает две compareTo функции, одну из которых принимает a, MyCompareAble а другую принимает Object и передает его первой после приведения.

Какие альтернативы необработанным типам: Используйте дженерики

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

Но если вы назначите необработанный тип параметризованному типу, вы получите предупреждение:

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

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

Раздел Erasure Type содержит больше информации о том, как компилятор Java использует необработанные типы.

Непроверенные сообщения об ошибках

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

Примечание: Example.java использует непроверенные или небезопасные операции.

Это может произойти при использовании старого API, который работает с необработанными типами, как показано в следующем примере:

Например, до того как Java-дженерики стали доступны, вы должны использовать класс коллекции следующим образом:

Когда вы добавляете свой объект в список, его не волнует, какой это тип объекта, и когда вы получаете его из списка, вы должны явно привести его к ожидаемому типу.

Используя дженерики, вы удаляете «неизвестный» фактор, потому что вы должны явно указать, какой тип объектов может идти в списке:

Обратите внимание, что при использовании дженериков вам не нужно приводить объект, полученный из вызова get, коллекция предопределена для работы только с MyObject. Этот факт является основным движущим фактором для дженериков. Это превращает источник ошибок времени выполнения во что-то, что можно проверить во время компиляции.

Вы должны указать тип-параметра.

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

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

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

Какая альтернатива, если мы не можем использовать необработанные типы, и как это лучше?

Например, для метода, в котором программист хочет убедиться, что переменная List с именем ‘names’ содержит только строки:

Источник

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

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