| Thinking in Java, 2nd edition -9 |
|
|
Sunday, 07 May 2006 | Bruce Eckel для раздела Ученье – свет Общие ловушки при использовании операторовОдна из ошибок при использовании операторов - это попытка обходится без круглых скобок, когда вы даже немного не представляете того, как будет вычисляться выражение. Это все еще верно в Java.
Чрезвычайно общая ошибка в C и C++ выглядит так:
while(x = y) {
// ....
}
Программист пробовал проверить на равенство (==), а выполнил присвоение. В C и C++ результат присвоения всегда будет true, если y не ноль, и вы, вероятно, получите бесконечный цикл. В Java, результат этого выражения не boolean, а компилятор ожидает boolean и не может преобразовать int, так что он выдаст вам ошибку времени компиляции и выявит проблему до того, как ы запустите программу. Так что ловушка никогда не случится в Java. (Вы не получите сообщение об ошибке времени компиляции, когда x и y - boolean, в таком случае x = y i- допустимое выражение, но в приведенном примере, вероятно, ошибочное.)
Аналогично C и C++ есть проблема использование битовыз И и ИЛИ вместо логической версии. Битовые И и ИЛИ используют один символ (& или |), а логические И и ИЛИ используют два (&& и ||). Как и с = и ==, легко напечатать только один символ вместо двух. В Java компилятор опять предотвратит это, потому что он не позволит вым бесцеремонно использовать один тип, где это не применимо.
Операторы приведения
Слово приведение используется в смысле “приведение к шаблону”. Java будет автоматически менять тип данных на другой при присвоении. Например, если вы присваиваете целочисленное значение переменной с плавающей точкой, компилятор автоматически конвертирует int в float. Приведение позволяет вам сделать такой тип преобразования более точным или форсировать его, когда оно не может выполнится нормально.
Для выполнения приведения поместите нужный тип данный (включая все модификаторы) внутри круглых скобок с левой стороны от любого значения. Вот пример:
void casts() {
int i = 200;
long l = (long)i;
long l2 = (long)200;
}
Как вы можете видеть, возможно выполнить приведение для числового значения так же как и для переменной. Однако, вобоих показанных здесь приведениях излишне, так как компилятор автоматически переводит значение int в long, когда это необходимо. Но вам позволено применять излишнее преобразование, чтобы сделать какое-то место или сделать ваш код более понятным. В остальных ситуациях приведение может быть очень важно просто для того, чтобы код скомпилировался.
В C и C++ приведение может стать причиной головной боли. В Java приведение безопасно, за исключением тех случаев, когда вы выполняете так называемое сужающее преобразование (то есть, когда вы переходите от одного типа данных, который содержит больше информации, к другому, который не содержит так много), вы рискуете потерять информацию. Здесь компилятор заставляет вас выполнить преобразование и при этом говорит: “выполнение это может быть опасным — если вы хотите от меня, чтобы я все равно сделал это, вы должны выполнить явное преобразование”. При расширенном преобразовании в явном приведении нет необходимости, потому что новый тип будет содержать больше информации, в отличае от старого типа, так что не будет потерь в информации.
Java позволяет вам выполнить приведение любого примитивного типа к любому другому примитивному типу, за исключением boolean, для которого не допускается любое приведение. Типы классов не позволяют приведение. Для преобразования одного к другому должны быть специальные методы. (String - особый случай и вы позже найдете в этой книге, что объекты могут приводится в пределах семейства типов; Oak может быть преобразован к Tree и наоборот, но не к постороннему типу, такому как Rock.)
Литералы
Обычно, когда вы вставляете литерное значение в программу, компилятор точно знает каким типом его сделать. Однако иногда тип неоднозначен. Когда это случается, вы должны указать компилятору дополнительную информацию в форме символов, ассоциированных со значением литерала. Приведенный ниже код показывает эти символы:
//: c03:Literals.java
class Literals {
char c = 0xffff; // максимальное шестнадцатиричное значение для char
byte b = 0x7f; // максимальное шестнадцатиричное значение для byte
short s = 0x7fff; // максимальное шестнадцатиричное значение для short
int i1 = 0x2f; // Шестнадцатирично-десятичное (в нижнем регистре)
int i2 = 0X2F; // Шестнацчатирично-десятичное (в верхнем регистре)
int i3 = 0177; // Восьмеричное (ведущий ноль)
// Шестнадцатиричные и восьмиричные также работают с long.
long n1 = 200L; // суффикс для long
long n2 = 200l; // суффикс для long
long n3 = 200;
//! long l6(200); // не допустимо
float f1 = 1;
float f2 = 1F; // суффикс для float
float f3 = 1f; // суффикс для float
float f4 = 1e-45f; // 10 - основание степени
float f5 = 1e+9f; // суффикс для float
double d1 = 1d; // суффикс для double
double d2 = 1D; // суффикс для double
double d3 = 47e47d; // 10 - основание степени
} ///:~
Шестнадцатерично-десятичные (основание 16), которые работают со всеми интегрированными типами данных, указываются лидирующим символом 0x или 0X, за которыми следует 0—9 и далее a—f в верхнем, либо в нижнем регистре. Если вы попробуете проинициализировать переменную с помощью значения, большего, чем она может принять (не зависимо от числовой формы значения), компилятор выдаст вам сообщение об ошибке. Обратите внимание в приведенном выше коде на максимально допустимое шестнадцатирично-десятичное значение для char, byte и short. Если вы превысите его, компилятор автоматически преобразует значение к int и скажет вам, что необходимо сужающее приведение для присваения. Вы найдете это место, остановившись на этой строке.
Восьмеричные (основание 8) указываются лидирующим нулемв цисле и цифррами 0-7. Нет специальных литералов для бинарного впедсталения в C, C++ или Java.
Замыкающие символы после литерного значения устанавливают тип. Символ L в верхнем или нижнем регистре означает long, верхний или нижний регистр F означает float, а верхний или нижний регистр D означает double.
Используется експонентная запись, которую я всегда находил пугающей: 1.39 e-47f. В науки и инженерии ‘e’ означает основание натурального логарифма, примерно 2.718. (Более точное значение типа double доступно в Java, как Math.E.) Здесь используется экспонентное выражение, такое как 1.39 x e-47, которое означает 1.39 x 2.718-47. Однако когда был создан FORTRAN, то решили, что e на самом деле будет означать “десять в степени”, что было странным решением, потому что FORTRAN был предназначен для науки и инжененрии, и можно подумать, что его разработчики будут чувствительны к введению такой неоднозначности. В любом случае это перешло в C, C++ и теперь в Java. Так что, если вы используете мышление в терминах e, как основания натурального логарифма, вы должны в уме выполнить перевод, когда используете такое выражение, как 1.39 e-47f в Java; это означает 1.39 x 10-47.
Обратите внимание, что вам нет необходимости использовать завершающий символ, когда компилятор может определить подходящий тип. В примере
long n3 = 200;
нет неоднозначности, так что L после 200 будет излишним. Однако в примере
float f4 = 1e-47f; // 10 в степени
компилятор обычно принимает експоненциальные числа как числа двойной точности, так что без завершающего f это даст вам ошибку, говорящую вам о том, что вы должны использовать приведение для преобразования double к float.
Повышение
Вы обнаружите, что если вы выполняете любую математическую или битовую операцию над примитивными типами данных, которые меньше, чем int (то есть, char, byte или short), эти значения будут повышены до int перед выполнением операций, а результирующее значение будет типа int. Так что, если вы хотите присвоить обратно к маленькому типу, вы должны использовать приведение. (И, так как вы обратно присваиваете к меньшему типу, вы можете потерять информацию.) В общем, большие типы данных в выражениях - это то, что определяет размер результата выражения; если вы умножаете float на double, результатом будет double; если вы складываете int и long, результатом будет long.
|