Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Лекция



Привет, Вы узнаете о том , что такое стирание типа, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое стирание типа, вывод типов, утиная типизация, приведение типа , настоятельно рекомендую прочитать все из категории ООП и практические JAVA.

стирание типа

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

Проблемы со стиранием типа

Обобщения проверяются на правильность типа во время компиляции. Затем информация об общем типе удаляется в процессе, называемом стиранием типа . Например, Стирание типа, Вывод типов, Утиная типизация, Приведение типа будет преобразован в неуниверсальный тип List, который обычно содержит произвольные объекты. Проверка во время компиляции гарантирует, что результирующий код является правильным по типу.

Из-за стирания типа параметры типа не могут быть определены во время выполнения. Например, когда ArrayList рассматривается во время выполнения, не существует общий способ , чтобы определить , является ли, до того типа стирания, это был Стирание типа, Вывод типов, Утиная типизация, Приведение типа или Стирание типа, Вывод типов, Утиная типизация, Приведение типа. Многих это ограничение не устраивает. Есть частичные подходы. Например, можно исследовать отдельные элементы, чтобы определить, к какому типу они принадлежат; например, если an ArrayList содержит Integer, то этот ArrayList мог быть параметризован Integer(однако он мог быть параметризован любым родительским элементом Integer, например Numberили Object).

Демонстрируя это, следующий код выводит "Equal":

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Другой эффект стирания типа заключается в том, что универсальный класс не может расширять класс Throwable каким-либо образом, прямо или косвенно:

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Причина, по которой это не поддерживается, связана со стиранием типа:

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

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

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

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

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

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

Вывод типа

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

вывод типов (англ. type inference) — в программировании возможность компилятора самому логически вывести тип значения у выражения. Впервые механизм вывода типов был представлен в языке ML, где компилятор всегда выводит наиболее общий полиморфный тип для всякого выражения. Это не только сокращает размер исходного кода и повышает его лаконичность, но и нередко повышает повторное использование кода .

Вывод типов характерен для функциональных языков программирования, хотя со временем он был частично реализован и в объектно-ориентированных языках (C#, D, Visual Basic .NET, Nim, C++11, Vala, Java[a]), где ограничивается возможностью опустить тип идентификатора в определении с инициализацией (см. синтаксический сахар). Например:

var s = "Hello, world!";  // Тип переменной s (от string) выведен исходя из инициализатора

Алгоритмы Вывода типа

Алгоритм Хиндли — Милнера

Алгоритм Хи́ндли — Ми́лнера — механизм вывода типов выражений, реализуемый в языках программирования, основанных на системе типов Хиндли — Милнера, таких как ML (первый язык этого семейства), Standard ML, OCaml, Haskell, F#, Fortress и Boo. Язык Nemerle использует этот алгоритм с рядом необходимых изменений .

Механизм вывода типов основан на возможности автоматически полностью или частично выводить тип выражения, полученного при помощи вычисления некоторого выражения. Так как этот процесс систематически производится во время трансляции программы, транслятор часто может вывести тип переменной или функции без явного указания типов этих объектов. Во многих случаях можно опускать явные декларации типов — это можно делать для достаточно простых объектов, либо для языков с простым синтаксисом. Например, в языке Haskell реализован достаточно мощный механизм вывода типов, поэтому указание типов функций в этом языке программирования не требуется. Программист может указать тип функции явно для того, чтобы ограничить ее использование только для конкретных типов данных, либо для более структурированного оформления исходного кода.

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

Сама модель типизации основана на алгоритме вывода типов выражений, который имеет своим источником механизм получения типов выражений, используемый в типизированном λ-исчислении, который был предложен в 1958 г. Х. Карри и Р. Фейсом. Далее уже́ Роджер Хиндли в 1969 г. расширил сам алгоритм и доказал, что он выводит наиболее общий тип выражения. В 1978 г. Робин Милнер независимо от Р. Хиндли доказал свойства эквивалентного алгоритма. И, наконец, в 1985 г. Луис Дамас окончательно показал, что алгоритм Милнера является законченным и может использоваться для полиморфных типов. В связи с этим алгоритм Хиндли — Милнера иногда называют также и алгоритмом Дамаса — Милнера.

Система типов определяется в модели Хиндли — Милнера следующим образом:

  1. Примитивные типы Стирание типа, Вывод типов, Утиная типизация, Приведение типа являются типами выражений.
  2. Параметрические переменные типов α являются типами выражений.
  3. Если Стирание типа, Вывод типов, Утиная типизация, Приведение типа и Стирание типа, Вывод типов, Утиная типизация, Приведение типа — типы выражений, то тип Стирание типа, Вывод типов, Утиная типизация, Приведение типа является типом выражений.
  4. Символ Стирание типа, Вывод типов, Утиная типизация, Приведение типа является типом выражений.

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

  1. Константы являются выражениями.
  2. Переменные являются выражениями.
  3. Если Стирание типа, Вывод типов, Утиная типизация, Приведение типа и Стирание типа, Вывод типов, Утиная типизация, Приведение типа — выражения, то (Стирание типа, Вывод типов, Утиная типизация, Приведение типа) — выражение.
  4. Если Стирание типа, Вывод типов, Утиная типизация, Приведение типа — переменная, а Стирание типа, Вывод типов, Утиная типизация, Приведение типа — выражение, то Стирание типа, Вывод типов, Утиная типизация, Приведение типа — выражение.

Говорят, что тип Стирание типа, Вывод типов, Утиная типизация, Приведение типа является экземпляром типа Стирание типа, Вывод типов, Утиная типизация, Приведение типа, когда имеется некое преобразование Стирание типа, Вывод типов, Утиная типизация, Приведение типа такое, что:

Стирание типа, Вывод типов, Утиная типизация, Приведение типа

При этом обычно полагается, что на преобразования типов Стирание типа, Вывод типов, Утиная типизация, Приведение типа накладываются ограничения, заключающиеся в том, что:

  1. Стирание типа, Вывод типов, Утиная типизация, Приведение типа
  2. Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Сам алгоритм вывода типов состоит из двух шагов — генерация системы уравнений и последующее решение этих уравнений.

Построение системы уравнений

Построение системы уравнений основано на следующих правилах:

  1. Стирание типа, Вывод типов, Утиная типизация, Приведение типа — в том случае, если связывание Стирание типа, Вывод типов, Утиная типизация, Приведение типа находится в Стирание типа, Вывод типов, Утиная типизация, Приведение типа.
  2. Стирание типа, Вывод типов, Утиная типизация, Приведение типа — в том случае, если Стирание типа, Вывод типов, Утиная типизация, Приведение типа, где Стирание типа, Вывод типов, Утиная типизация, Приведение типа и Стирание типа, Вывод типов, Утиная типизация, Приведение типа.
  3. Стирание типа, Вывод типов, Утиная типизация, Приведение типа — в том случае, если Стирание типа, Вывод типов, Утиная типизация, Приведение типа, где Стирание типа, Вывод типов, Утиная типизация, Приведение типа это Стирание типа, Вывод типов, Утиная типизация, Приведение типа с добавленным связыванием Стирание типа, Вывод типов, Утиная типизация, Приведение типа.

В этих правилах под символом {\displaystyle \Gamma }Стирание типа, Вывод типов, Утиная типизация, Приведение типа понимается набор связываний переменных с их типами:

Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Решение системы уравнений

Решение построенной системы уравнений основано на алгоритме унификации. Об этом говорит сайт https://intellect.icu . Это достаточно простой алгоритм. Имеется некоторая функция {\displaystyle u}Стирание типа, Вывод типов, Утиная типизация, Приведение типа, которая принимает на вход уравнение типов и возвращает подстановку, которая делает левую и правую части уравнения одинаковыми («унифицирует» их). Подстановка — это просто проекция переменных типов на сами типы. Такие подстановки могут вычисляться различными способами, которые зависят от конкретной реализации алгоритма Хиндли — Милнера.

утиная типизация

Неявная типизация, латентная типизация или утиная типизация (англ. Duck typing) в ООП-языках — определение факта реализации определенного интерфейса объектом без явного указания или наследования этого интерфейса, а просто по реализации полного набора его методов.

Название утинной типизации

Название термина пошло от английского «duck test» («утиный тест»), который в оригинале звучит как:

Если это выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка.

If it looks like a duck, swims like a duck and quacks like a duck, then it probably is a duck.

Принцип утинной типизации

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

Такой подход позволяет полиморфно работать с объектами, которые не связаны в иерархии наследования. Достаточно, чтобы все эти объекты поддерживали необходимый набор методов.

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

Проблемы иерархической типизации

Утиная типизация решает такие проблемы иерархической типизации, как:

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

Утиная типизация практически незаменима в прикладных языках сценариев («скриптов»), где приходится работать с внешними по отношению к программе (скрипту) документами (веб-страницы, текстовые и табличные документы), иерархия объектов которых недоступна.

Языки, поддерживающие утиную типизацию

Утиная типизация поддерживается в том числе в языках: Prolog, D, Perl, Smalltalk, Python, Objective-C, Ruby, JavaScript, TypeScript, Groovy, ColdFusion, Boo, Lua, Go, Scala.

приведение типа

Приведение (преобразование) ти́па (англ. type conversion, typecasting, coercion) — в информатике преобразование значения одного типа в значение другого типа.

Описание Приведение типа

Выделяют приведения типов:

  • явные (англ. explicit);
  • неявные (англ. implicit).

Явное приведение задается программистом в тексте программы с помощью:

  • конструкции языка;
  • функции, принимающей значение одного типа и возвращающей значение другого типа.

Неявное приведение выполняется транслятором (компилятором или интерпретатором) по правилам, описанным в стандарте языка. Стандарты большинства языков запрещают неявные преобразования.

В слабо типизированных объектно-ориентированных языках, таких как C++, механизм наследования реализуется посредством приведения типа указателя на текущий объект к базовому классу (в типобезопасных, таких как OCaml, понятие о приведении типов отсутствует принципиально, и допустимость обращения к компоненту подтипа контролируется механизмом проверки согласования типов на этапе компиляции, а в машинном коде остается прямое обращение).

Неявное приведение типа

Неявное приведение типа в языках C/C++

Неявное приведение типов происходит в следующих случаях:

  • после вычисления операндов бинарных арифметических, логических, битовых операций, операций сравнения, а также 2-го или 3-го операнда операции «?:»; значения операндов приводятся к одинаковому типу;
  • перед выполнением присваивания;
  • перед передачей аргумента функции;
  • перед возвратом функцией возвращаемого значения;
  • после вычисления выражения конструкции switch значение приводится к целочисленному типу;
  • после вычисления выражений конструкций if, for, while, do-while значение приводится к типу bool.

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

Рассмотрим пример на языке C.

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

При выполнении операций сравнения и при присваивании переменные разных типов неявно приводятся к одному типу.

При неявных преобразованиях возможны побочные эффекты. Например, при приведении числа вещественного типа к целому типу дробная часть отсекается (округление не выполняется) . При обратном преобразовании возможно понижение точности из-за различий в представлении вещественных и целочисленных чисел. Например, в переменной типа float (число с плавающей точкой одинарной точности по стандарту IEEE 754), нельзя сохранить число 16 777 217 без потери точности, а в 32-битной переменной целого типа int — можно. Из-за потери точности операции сравнения одного и того же числа, представленного целым и вещественным типами (например, int и float), могут давать ложные результаты (числа могут быть не равны).

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Приведенный код выведет следующее, если размер int — 32 бита и компилятор поддерживает стандарт IEEE 754:

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Явное приведение типа

Приведения типов в языке C

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

int X;
int Y = 200;
char C = 30;
X = (int)C * 10 + Y; // переменная С приводится к типу int

Для вычисления последнего выражения компилятор выполняет примерно следующие действия:

  • сначала переменная C символьного типа char явно приводится к целочисленному типу int путем расширения разрядности;
  • выполняется вычисление операндов для операции умножения. Левый операнд имеет тип int. Правый операнд — константа 10, а такие константы по умолчанию имеют тип int. Так как оба операнда оператора «*» имеют тип int, неявное приведение типов не выполняется. Результат умножения тоже имеет тип int;
  • выполняется вычисление операндов операции сложения. Левый операнд — результат умножения имеет тип int. Правый операнд — переменная Y имеет тип int. Так как оба операнда оператора «+» имеют тип int, неявное приведение к общему типу не выполняется. Результат сложения тоже имеет тип int;
  • выполнение присваивания. Левый операнд — переменная X имеет тип int. Правый операнд — результат вычисления выражения, записанного справа от знака «=», тоже имеет тип int. Так как оба операнда оператора «=» имеют одинаковый тип, неявное приведение типов не выполняется.

Но даже при этом возможны ошибки. Тип char может быть как знаковым (signed char), так и беззнаковым (unsigned char); результат зависит от реализации компилятора и такое поведение разрешено стандартом. Значение беззнакового типа char при преобразовании к знаковому типу int может оказаться отрицательным из-за особенностей реализации машинных инструкций на некоторых процессорах. Чтобы избежать неоднозначностей, рекомендуется явно указывать знаковость для типа char.

Приведения типов в языке C++

В языке C++ существует пять операций для явного приведения типа. Первая операция — круглые скобки ((type_to)expression_from) поддерживается для сохранения совместимости с C. Остальные четыре операции записываются в виде

Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Рассмотрим пример.

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа // переменной y будет присвоено значение -2

Громоздкие ключевые слова являются напоминанием программисту о том, что приведение типа чревато проблемами.

Операция static_cast

Назначение: допустимые приведения типов.

Операция static_cast аналогична операции «круглые скобки» с одним исключением: она не выполняет приведение указателей на неродственные типы (для этого применяется операция reinterpret_cast).

Применение:

  • преобразование между числовыми и enum, в том числе если неявное преобразование невозможно (int enum class) или приводит к предупреждению «Возможная потеря точности» (double float);
  • приведение указателей к типу void* и наоборот;
  • приведение указателей на производные типы к указателям на базовые типы и наоборот;
  • выбор одной из нескольких перегруженных функций;
 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

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

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа
  • приведение типа в шаблонах (компилятор уже при специализации шаблона решает, какие операции использовать);
  • приведение операндов тернарной условной операции «?:» к одному типу (значения 2-го и 3-го операндов должны иметь одинаковый тип);

Ограничения на expression_from: нет.

Ограничения на type_to: должен существовать способ преобразования значения выражения expression_from к типу type_to, с помощью operator type_to или конструктора.

Производит ли операция static_cast код: в общем случае да (например, вызов перегруженной операции приведения типа или конструктора).

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

Примеры.

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Операция dynamic_cast

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

Операция получает информацию о типе объекта expression_from с помощью RTTI. Если тип будет type_to или его подтипом, приведение выполняется. Иначе:

  • для указателей возвращается NULL;
  • для ссылок создается исключение std::bad_cast.

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

Ограничения на type_to: ссылка или указатель на дочерний по отношению к expression_from тип.

Производит ли операция dynamic_cast код: да.

Логические ошибки возможны, если операции передать аргумент, не имеющий тип type_to, и не проверить указатель на равенство NULL (соответственно не обработать исключение std::bad_cast).

Операция const_cast

Назначение: снятие/установка модификатора(ов) const, volatile и/или mutable. Часто это применяется, чтобы обойти неудачную архитектуру программы или библиотеки, для стыковки Си с Си++, для передачи информации через обобщенные указатели void*, для одновременного написания const- и не-const-версии функции (в Си++14 существует обход через decltype(auto) ).

Ограничения на expression_from: выражение должно возвращать ссылку или указатель.

Ограничения на type_to: тип type_to должен совпадать с типом выражения expression_from с точностью до модификатора(ов) const, volatile и mutable .

Производит ли операция const_cast код: нет.

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

Для примера рассмотрим код динамической библиотеки.

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

При загрузке библиотеки в память процесса создает новый сегмент данных, в котором размещаются глобальные переменные. Код функции SomeDllFunction() находится в библиотеке и при вызове возвращает указатель на скрытый член глобального объекта класса string. Операция const_cast используется для удаления модификатора const.

Операция reinterpret_cast

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

Объект, возвращаемый выражением expression_from, рассматривается как объект типа type_to.

Ограничения на expression_from: выражение должно возвращать значение порядкового типа (любой из целых, логический bool или перечислимый enum), указатель или ссылку.

Ограничения на type_to:

  • Если expression_from возвращает значение порядкового типа или указатель, тип type_to может быть порядковым типом или указателем.
  • Если expression_from возвращает ссылку, тип type_to должен быть ссылкой.

Производит ли операция reinterpret_cast код: нет.

Источники логических ошибок. Объект, возвращаемый выражением expression_from, может не иметь типа type_to. Нет никакой возможности проверить это, всю ответственность за корректность преобразования программист берет на себя.

Рассмотрим примеры.

 Стирание типа, Вывод типов, Утиная типизация, Приведение типа

Вау!! 😲 Ты еще не читал? Это зря!

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

создано: 2021-11-26
обновлено: 2023-08-10
132265



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

ООП и практические JAVA

Термины: ООП и практические JAVA