Автоматическое преобразование типов в операции присваивания выполняется при соблюдении следующих условий:
оба типа являются совместимыми;
целевой тип обладает более широким диапазоном допустимых значений, чем исходный тип.
Если оба перечисленных выше условия соблюдаются, происходит расширяющее преобразование типов. Например, диапазона значений, допустимых для типа int, совершенно достаточно, чтобы представить любое значение типа byte, а кроме того, оба эти типа данных являются целочисленными. Поэтому и происходит автоматическое преобразование типа byte в тип int.
С точки зрения расширяющего преобразования типов целочисленные типы и типы с плавающей точкой совместимы друг с другом. Например, приведенная ниже программа написана корректно, поскольку преобразование типа long в тип double является расширяющим и выполняется автоматически. // Демонстрация автоматического преобразования типа long в тип double, class LtoD { public static void main(String args[]) { long L; double D; L = 100123285L; // Автоматическое преобразование типа long в тип double. D = L; System.out.println("L and D: " + L + " " + D); } }
В то же время тип double не может быть автоматически преобразован в тип long, поскольку такое преобразование уже не является расширяющим. Следовательно, приведенный ниже вариант той же самом программы оказывается некорректным. // *** Эта программа не подлежит компиляции. *** class LtoD { public static void main(String args[]) { long L; double D; D = 100123285.0; // Тип double не преобразуется автоматически в тип long. L = D; // Ошибка!!! System.out.println("L and D: " + L + " " + D) ; } }
Автоматическое преобразование числовых типов в тип char или boolean не производится. Кроме того, типы char и boolean несовместимы друг с другом. Тем не менее переменной char может быть присвоено значение, представленное целочисленным литералом. Приведение несовместимых типов
Несмотря на всю полезность неявных автоматических преобразований типов, они неспособны удовлетворить все потребности в программировании, поскольку допускают лишь расширяющие преобразования совместимых типов. А во всех остальных случаях приходится обращаться к приведению типов. Приведение — это команда компилятору преобразовать результат вычисления выражения в указанный тип. А для этого требуется явное преобразование типов. Ниже показана общая форма приведения типов. (целевой_тип) выражение
где целевой_тип обозначает тот тип, в который желательно преобразовать указанное выражение. Так, если требуется привести значение, возвращаемое выражением х / у, к типу int, это можно сделать следующим образом: double х, у; // ... (int) (х / у)
В данном случае приведение типов обеспечит преобразование результатов выполнения выражения в тип int, несмотря на то, что переменные х и у принадлежат к типу double. Выражение х / у следует непременно заключить в круглые скобки, иначе будет преобразован не результат деления, а только значение переменной х. Приведение типов в данном случае требуется потому, что автоматическое преобразование типа double в тип int не выполняется.
Если приведение типов приводит к сужающему преобразованию, то часть информации может быть потеряна. Например, в результате приведения типа long к типу int часть информации потеряется, если значение типа long окажется больше диапазона представления чисел для типа int, поскольку старшие разряды этого числового значения отбрасываются. Когда же значение с плавающей точкой приводится к целочисленному, в результате усечения теряется дробная часть этого числового значения. Так, если присвоить значение 1,23 целочисленной переменной, то в результате в ней останется лишь целая часть исходного числа (1), а дробная его часть (0,23) будет потеряна.
Ниже приведен пример программы, демонстрирующий некоторые виды преобразований, требующие явного приведения типов. // Демонстрация приведения типов, class CastDemo { public static void main(String args[]) { double x, y; byte b; int i; char ch; x = 10.0; У = 3.0; // В данном случае теряется дробная часть числа. i = (int) (х / у); // привести тип double к типу int System.out.println("Integer outcome of x / y: " + i) ; i = 100; // А в этом случае данные не теряются. Тип byte может // содержать значение 100. Ъ = (byte) i; System.out.println ("Value of b: " + b) ; i = 257; //На этот раз данные теряются. Тип byte не может // содержать значение 257. b = (byte) i; System.out.println("Value of b: " + b); b = 88; // Представление символа X в коде ASCII. //И снова требуется явное приведение несовместимых типов. ch = (char) b; System.out.println("ch: " + ch); } }
Выполнение этой программы дает следующий результат: Integer outcome of х / у: 3 Value of b: 100 Value of b: 1 ch: X
В данной программе приведение выражения (х / у) к типу int означает потерю дробной части числового значения результата деления. Когда переменной b присваивается значение 100 из переменной i, данные не теряются, поскольку диапазон допустимых значений у типа byte достаточен для представления этого значения. Далее при попытке присвоить переменной b значение 257 снова происходит потеря данных, поскольку значение 257 оказывается за пределами диапазона допустимых значений для типа byte. И наконец, когда переменной char присваивается содержимое переменной типа byte, данные не теряются, но явное приведение типов все же требуется. Предшествование операторов
В табл. 2.3 приведен порядок предшествования всех операторов в Java: от самого высокого до самого низкого. В эту таблицу включен ряд операторов, рассматриваемых далее в этой книге. Формально разделители [], () и . могут действовать как операторы, и в этом случае они будут иметь наивысший порядок предшествования.
Таблица 2.3. Предшествование операторов в Java Наивысший порядок ++ (постфиксный) -- (постфиксный) ++ (префиксный) -- (префиксный) ~ ! + (унарный плюс) - (унарный минус) (приведение типов) * / % + - >> <<< << > >= < <= instanceof == != & ^ | && || ?: = op=
Пример для опробования 2.2. Отображение таблицы истинности для логических операторов
В этом проекте предстоит создать программу, которая отображает таблицу истинности для логических операторов Java. Для удобства восприятия отображаемой информации следует выровнять столбцы таблицы. В данном проекте используется ряд языковых средств, рассмотренных ранее в этой главе, включая управляющие последовательности и логические операторы, а также демонстрируются отличия в предшествовании арифметических и логических операторов.
Последовательность действий
Создайте новый файл LogicalOpTable. java.
Для того чтобы обеспечить выравнивание столбцов таблицы, в каждую выводимую строку следует ввести символы \t. В качестве примера ниже приведен вызов метода println () для отображения заголовков таблицы. System.out.println(nP\tQ\tAND\tOR\tXOR\tNOT");
Для того чтобы сведения об операторах располагались под соответствующими заголовками, в каждую последующую строку таблицы должны быть введены символы табуляции.
Введите в файл LogicalOpTable . j ava исходный код программы, как показано ниже. // Пример для опробования 2.2. // Отображение таблицы истинности для логических операторов, class LogicalOpTable { public static void main(String args[]) { boolean p, q; System.out.println(MP\tQ\tAND\tOR\tXOR\tNOT"); p = true; q = true; System.out.print(p + "\tM + q +"\tn); System.out.print((p&q) + "\t" + (plq) + "\t"); System.out.println((pAq) + "\t" + (Ip)); p = true; q = false; System.out.print(p + "\t" + q +"\t"); System.out.print((p&q) + "\t" + (plq) + "\t"); System.out.println((pAq) + "\t" + (!p)); p = false; q = true; System, out .print (p + n\t" + q +"\t,f); System.out.print((p&q) + "\t" + (plq) + "\t"); System.out.println((pAq) + M\t" + (!p)); p = false; q = false; System.out.print(p + "\t" + q +"\t"); System.out.print((p&q) + "\t" + (plq) + "\t"); System.out.println((pAq) + "\t" + (!p)); } }