Автор: Widowmaker1984

Вопрос 6 (преобразования примитивных типов)

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

1. В Java предусмотрено семь видов приведений:

- тождественное (identity);
- расширение примитивного типа (widening primitive);
- сужение примитивного типа (narrowing primitive);
- расширение объектного типа (widening reference);
- сужение объектного типа (narrowing reference);
- преобразование к строке (String);
- запрещенные преобразования (forbidden).

1.1. Самым простым является тождественное преобразование. В Java преобразование выражения любого типа к точно такому же типу всегда допустимо и успешно выполняется. С теоретической точки зрения теперь можно утверждать, что любой тип в Java может участвовать в преобразовании, хотя бы в тождественном. Например, примитивный тип boolean нельзя привести ни к какому другому типу, кроме него самого.

2. Иногда возникают ситуации, когда переменной одного типа нужно присвоить значение другого типа. Для некоторых типов это можно проделать и без приведения, в таких случаях говорят об автоматическом преобразовании. В Java оно возможно лишь в случае, когда точности представления чисел переменной-приемника достаточно для хранения исходного значения. Например, при занесении значения переменной типа byte или short в переменную типа int.

скрытый текстЭто называется "расширением" (widening) или "повышением" (promotion), поскольку тип меньшей разрядности "расширяется" ("повышается") до большего совместимого типа. Размера типа int всегда достаточно для хранения чисел из диапазона, допустимого для типа byte, поэтому в подобных ситуациях оператора явного приведения типа не требуется. Обратное в большинстве случаев неверно, поэтому для занесения значения типа int в переменную типа byte необходимо использовать оператор приведения типа. Эту процедуру иногда называют "сужением" (narrowing), поскольку транслятору явно сообщается, что величину необходимо преобразовать, чтобы она уместилась в переменную нужного типа. Для этого перед ней нужно указать данный тип, заключенный в круглые скобки.

int a = 100;
byte b = (byte) a;


2.1. Следующие 19 преобразований являются расширяющими:
- от byte к short, int, long, float, double
- от short к int, long, float, double
- от char к int, long, float, double
- от int к long, float, double
- от long к float, double
- от float к double

2.1.1. Нельзя провести преобразование к типу char от типов меньшей или равной длины (byte, short), или, наоборот, к short от char без потери данных. Это связано с тем, что char, в отличие от остальных целочисленных типов, является беззнаковым.

2.1.2. Даже при расширении данные все-таки могут быть в особых случаях искажены. Это приведение значений int к типу float и приведение значений типа long к типу float или double. Хотя эти дробные типы вмещают гораздо большие числа, чем соответствующие целые, но у них меньше значащих разрядов.

long a=111111111111L;
float f = a;
a = (long) f;
print(a);


Результатом будет: 111111110656

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

byte a = 40;
byte b = 50;
byte с = 100;
int d = a* b / с;


Результат промежуточного выражения (а*b) вполне может выйти за диапазон допустимых для типа byte значений. Именно поэтому Java автоматически повышает тип каждой части выражения до типа int, так что для промежуточного результата (а*b) хватает места.

3.1. Если в выражении используются переменные типов byte, short и int, то во избежание переполнения тип всего выражения автоматически повышается до int. Если же в выражении тип хотя бы одной переменной — long, то и тип всего выражения тоже повышается до long. Надо учитывать, что целые литералы, в конце которых не стоит суффикс L, имеют тип int.

3.2. Если выражение содержит операнды типа float, то и тип всего выражения автоматически повышается до float. Если же хотя бы один из операндов имеет тип double, то тип всего выражения повышается до double. По умолчанию Java рассматривает все литералы с плавающей точкой, как имеющие тип double. Приведенная ниже программа показывает, как повышается тип каждой величины в выражении для достижения соответствия со вторым операндом каждого бинарного оператора.

3.3. Автоматическое преобразование типа иногда может оказаться причиной неожиданных сообщений транслятора об ошибках. Например, показанный ниже код, хотя и выглядит вполне корректным, приводит к сообщению об ошибке на фазе трансляции. В нем значение 50*2, которое должно уместиться в тип byte, записывается в байтовую переменную. Из-за автоматического преобразования типа результата в int получаем сообщение об ошибке от транслятора — ведь при занесении int в byte может произойти потеря точности.

byte b = 50;
b = b* 2;

^ Incompatible type for =. Explicit cast needed to convert int to byte.
(Несовместимый тип для =. Необходимо явное преобразование int в byte)


Исправленный текст :
byte b = 50;
b = (byte) (b* 2);


4. Если попытаться поместить в переменную-контейнер то, что туда не помещается, то в ней останется лишь то, что туда «влезло». К примеру, у чисел с плавающей точкой будет «отсекаться» дробная часть.

double a=11.2345;
int b=(int)a;
System.out.println(b); // в консоли получится число 11


На первом шаге дробное значение преобразуется в long, если целевым типом является long, или в int - в противном случае (целевой тип byte, short, char или int). Для этого исходное дробное число сначала математически округляется в сторону нуля, то есть дробная часть просто отбрасывается.

4.1. При этом могут возникнуть особые случаи:

- если исходное дробное значение является NaN, то результатом первого шага будет 0 выбранного типа (т.е. int или long);
- если исходное дробное значение является положительной или отрицательной бесконечностью, то результатом первого шага будет, соответственно, максимально или минимально возможное значение для выбранного типа (т.е. для int или long);
- наконец, если дробное значение было конечной величиной, но в результате округления получилось слишком большое по модулю число для выбранного типа (т.е. для int или long), то, как и в предыдущем пункте, результатом первого шага будет, соответственно, максимально или минимально возможное значение этого типа. Если же результат округления укладывается в диапазон значений выбранного типа, то он и будет результатом первого шага.

4.2. Надо учитывать, что дробная часть не округляется, а именно отбрасывается. Т.е. 2.7 -> 2. А если в byte (диапазон от -128 до 127) положить число 128, то результатом будет не 1, а -128 (значения старших битов будут потеряны). Значение переменной при таком преобразовании можно рассчитать, но цель программиста – не допускать ситуации, когда значение выходит за допустимые границы, поскольку это может привести к неправильной работе программы. Если число больше своего контейнера, результат будет непредсказуемым.

4.2. Следующие 23 преобразования являются сужающими:
- от byte к char
- от short к byte, char
- от char к byte, short
- от int к byte, short, char
- от long к byte, short, char, int
- от float к byte, short, char, int, long
- от double к byte, short, char, int, long, float

Комментарии


Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)