Динамическое связывание типов
Такое присваивание может связать переменную с новым адресом и новой ячейкой памяти, потому что форматы хранения величин в разных типах отличаются друг от друга. Динамическое связывание постулирует: любой переменной может быть присвоено значение любого типа. Более того, во время выполнения программы тип переменной может меняться многократно. Важно понимать, что тип у переменной с динамически связанным типом имеет лишь временный характер.
226 |
Глава 8. Типизация данных |
Если тип связан с переменной статически, то имя переменной навсегда привязано к этому типу. Напротив, если тип связан с переменной динамически, то имя переменной лишь временно привязано к типу. На самом деле имена таких переменных никогда не ограничены типами. Более точно: имена могут быть связаны с переменными, а переменные могут быть связаны с типами. Языки с динамической типизацией существенно отличаются от языков со статическим связыванием типов. Основное преимущество динамического связывания переменных с типом заключается в повышении гибкости программирования. В частности, программу обработки числовых данных в языке, применяющем динамическое связывание типов, можно написать в форме настраиваемой программы. Такая программа сможет работать с данными любого числового типа. Любой тип входных данных считается приемлемым, поскольку переменные, предназначенные для их хранения, будут привязываться к этому типу в ходе присваивания. Напротив, статическое связывание типов не позволяет написать на языке С программу для обработки данных без фиксации их типа.До середины 1990-х годов самые популярные языки программирования повсеместно использовали статическое связывание типа, редким исключением были некоторые функциональные языки, такие как LISP. Однако с тех пор произошел существенный сдвиг в сторону динамического связывания типов. В языках Python, Ruby, JavaScript и PHP теперь принято динамическое связывание типа. Например, программа на языке JavaScript может содержать следующий оператор: list = [4.3, 8.2]; Независимо от предыдущего типа переменной list, в результате такого присваивания она превратится в одномерный массив из двух элементов, являющихся числами с плавающей точкой. Если же далее последует оператор list = 75; то переменная list станет целочисленной скалярной переменной. Возможность динамического связывания типа была введена в язык C# версии 2010 года. Включение в объявление переменной зарезервированного слова dynamic разрешает динамическое связывание типа, это показано в следующем примере: dynamic any; Можно отметить некоторое сходство данного объявления с объявлением object any; Объявления схожи в том, что переменной any может быть присвоено значение любого типа (даже типа object), как и для объявления с фиксированным типом object. Различие же заключается в исключении возможностей взаимодействия с фрагментами на таких динамически типизированных языках, как IronPython и IronRuby (.NET версий языков Python и Ruby соответственно). Тем не менее это полезно, если данные неизвестного типа поступили в программу из внешнего источника. Члены класса, свойства, параметры метода, возвращаемые методом величины и локальные переменные — все они могут быть объявлены динамичными. В чисто объектно-ориентированных языках — например, Ruby — все переменные являются ссылками и не имеют типов, а все данные считаются объектами, и любая |
Атрибуты переменной |
227 |
переменная может ссылаться на любой объект. В некотором смысле, все переменные в таких языках имеют одинаковый тип — это ссылки. Однако в отличие от ссылок в языке Java, которые ограничены возможностью ссылаться лишь на один конкретный тип, переменные в Ruby могут ссылаться на любой объект. В заключение еще раз обсудим недостатки динамического связывания типов. Прежде всего снижается надежность программ, поскольку возможности обнаружения ошибок много меньшие, чем у компилятора для языка со статическим связыванием типов. Динамическое связывание типов позволяет присвоить любой переменной значение любого типа. В этом случае неправильные типы на правой стороне оператора присваивания не будут распознаны как ошибки, вместо этого тип переменной-приемника изменится на этот неправильный тип. Допустим, что в конкретной программе на JavaScript имеются две скалярные числовые переменные а и b, а также массив m. Допустим также, что в программе должен быть оператор присваивания: a = b; Вместо него был введен неправильный оператор: a = m; В языке JavaScript (или любом другом языке с динамическим связыванием типов) интерпретатор ошибку не обнаружит. Тип переменной а просто будет изменен на тип массива. Поскольку при дальнейшем использовании переменной а ожидалось скалярное значение, результаты станут непредсказуемыми. В языке со статическим связыванием типов, таком как Java, компилятор обнаружит ошибку а = m, и программа не будет выполнена. Отметим, что этот недостаток частично присутствует и в языках со статическим связыванием типов (Fortran, С и С++), которые в некоторых случаях автоматически приводят результат из правой части оператора присваивания к типу переменной из его левой части. Самым большим недостатком динамического связывания типов является его стоимость. Стоимость реализации динамического связывания атрибутов весьма значительна, особенно во время вычислений. Именно в это время должна выполняться проверка типов. Для ее обеспечения каждая переменная несет в себе сложный дескриптор. Для хранения любой переменной задействуется область памяти переменного размера, поскольку формат сохраняемого значения меняется от типа к типу. В языках с динамическим связыванием типов чаще всего используются интерпретаторы. У аппаратных средств компьютера нет команд, типы операндов которых неизвестны в период компиляции. Потому компилятор не может создавать машинные команды для выражения x + у, где типы x и у неизвестны в период компиляции. Чистая интерпретация, как правило, выполняется в 10 раз медленнее, чем эквивалентный машинный код. Конечно, если язык реализуется чистым интерпретатором, то время на выполнение динамического связывания типов скрыто и кажется более дешевым. С другой стороны, в языках со статическим связыванием типов обычно применяются компиляторы, так как программы на этих языках транслируются в очень эффективный машинный код. |
228 |
Глава 8. Типизация данных |