Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
А. Н. Васильев - Java. Объектно-ориентированное...doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
686.59 Кб
Скачать

Приведение типов

Я там столкнулся с одним очень нахальным типом.

Из к/ф «Приключения Шерлока Холмса

и доктора Ватсона»

Строгая типизация переменных вместе с очевидными преимуществами при- вносит и ряд не столь очевидных проблем. Поясним это на простом при- мере. Предположим, что в программе объявлены две числовые переменные: одна типа int и другая типа double. Переменным присвоены значения. Далее мы хотим к переменной типа double прибавить значение переменной типа int и результат записать в первую переменную. С формальной точки зрения здесь нет никакой проблемы, поскольку целые числа являются подмноже- ством множества действительных чисел. С точки зрения программной ло- гики ситуация не такая простая, ведь складываются переменные разных ти- пов. Понятно, что на самом деле здесь проблемы не возникает и описанную операцию можно выполнить (в том числе и в Java), причем возможность выполнения подобного рода операций достижима благодаря автоматическо- му приведению типов. Другими словами, если нужно вычислить выражение, в которое входят переменные разных типов, автоматически выполняется пре- образование входящих в выражение переменных к общему формату. Процесс автоматического преобразования типов подчиняется нескольким базовым пра- вилам. Вот они.

Типы переменных, входящих в выражение, должны быть совместимыми. Например, целое число можно преобразовать в формат действительного чис- ла, чего не скажешь о текстовой строке.

Целевой тип (тип, к которому выполняется приведение) должен быть «шире» исходного типа. Другими словами, преобразование должно выполняться без потери данных.

Перед выполнением арифметической операции типы byte, short и char рас- ширяются до типа int.

Если в выражении есть операнды типа long, то расширение осуществляется до типа long.

Если в выражении есть операнды типа float, то расширение осуществляется до типа float.

Если в выражении есть операнды типа double, то расширение осуществляется до типа double.

К этим правилам следует добавить не менее важные правила интерпретации ли- тералов. Действительно, как следует рассматривать, например, число (литерал) 2? Как значение типа int, типа long или, например, типа double? Следующие прави- ла дают ответы на подобные вопросы.

Литералы, обозначающие целые числа, интерпретируются как значения типа

int.

Литералы, обозначающие действительные числа, интерпретируются как зна- чения типа double.

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

byte a=1,b=2,c;

// Ошибка: c=a+b;

Ошибку вызывает последняя команда. Хотя все три переменные относятся к типу byte, при вычислении выражения a+b выполняется автоматическое преобразова- ние к типу int. В результате имеет место попытка присвоить значение типа int переменной типа byte. Поскольку в Java преобразования с возможной потерей точности не допускаются, программа с таким кодом не скомпилируется.

Еще один пример ошибки, связанной с автоматическим преобразованием ти- пов:

float x=2.7;

В данном случае проблема связана с тем, что литерал 2.7, использованный для инициализации переменной x типа float, интерпретируется как значение типа double.

Для обработок ошибок подобного рода, а также для ряда других целей в Java предусмотрено явное приведение типов и явное определение типа литерала с помощью суффиксов типа.

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

byte a=1,b=2,c;

// Нет ошибки – явное приведение типа: c=(byte)(a+b);

Командой (byte)(a+b) вычисляется сумма значений переменных a и b, а резуль- тат преобразуется к типу byte. Поскольку в правой части от оператора при- сваивания стоит переменная того же типа, проблем не возникает. Тем не менее следует понимать, что явное приведение типа потенциально опасно, поскольку может приводить к потере значения. Такие ситуации должен отслеживать про- граммист — системой они не отслеживаются.

Аналогичную процедуру можно применять и к литералам. Кроме того, изменять тип литералов можно с помощью суффиксов. Так, суффикс L у целочисленного литерала (например, 123L) означает, что он принадлежит к типу long, а суф- фикс F у литерала, обозначающего действительное число (например, 12.5F), озна- чает, что этот литерал относится к типу float. В свете сказанного корректными являются такие команды:

float x=2.7F;

float x=(float)2.7;

Кроме прочего, явное приведение типов часто используется вместе с операто- ром деления.

В Java, как и в С++, допускается динамическая инициализация переменных. При динамической инициализации значение переменной присваивается при объявлении, причем значением является выражение, содержащее другие пере- менные. Пример динамической инициализации переменной:

int a=3,b=4; int c=a*a+b*b;

В данном случае переменная c инициализируется выражением a*a+b*b, то есть получает значение 25. Главное и единственное условие для динамической ини- циализации — все переменные, входящие в соответствующее выражение, долж- ны быть предварительно объявлены и им должны быть присвоены значения.