13

13


Тернарный оператор «если-иначе»



Тернарный оператор необычен тем, что он использует три операнда. И все же это действительно оператор, так как он производит значение, в отличие от обычной конструкции выбора if-else, описанной в следующем разделе. Выра¬жение записывается в такой форме:



логическое-условие ? выражениеО : выражение1



Если логическое-условие истинно (true), то затем вычисляется выражениеО, и именно его результат становится результатом выполнения всего оператора. Если же логическое-условие ложно (false), то вычисляется выражение1, и его зна¬чение становится результатом работы оператора.



Конечно, здесь можно было бы использовать стандартную конструкцию if-else (описываемую чуть позже), но тернарный оператор гораздо компактнее. Хотя С (где этот оператор впервые появился) претендует на звание лаконичного языка, и тернарный оператор вводился отчасти для достижения этой цели, будьте благоразумны и не используйте его всюду и постоянно — он может ухуд¬шить читаемость программы.



Операторы + и += для String



В Java существует особый случай использования оператора: операторы + и += могут применяться для конкатенации (объединения) строк, и вы уже это видели. Такое действие для этих операторов выглядит вполне естественно, хотя оно и не соответствует традиционным принципам их использования.



При создании С++ в язык была добавлена возможность перегрузки операто¬ров, позволяющей программистам С++ изменять и расширять смысл почти лю¬бого оператора. К сожалению, перегрузка операторов, в сочетании с некоторы¬ми ограничениями С++, создала немало проблем при проектировании классов. Хотя реализацию перегрузки операторов в Java можно было осуществить проще, чем в С++ (это доказывает язык С#, где существует простой механиз перегруз¬ки), эту возможность все же посчитали излишне сложной, и поэтому програм¬мистам на Java не дано реализовать свои собственные перегруженные опера¬торы, как это делают программисты на С++.



Использование + и += для строк (String) имеет интересные особенности. Если выражение начинается строкой, то все последующие операнды также должны быть строками (помните, что компилятор превращает символы в ка¬вычках в объект String).



int х = 0. у = 1. z = 2;



String s = "х. у, z



System.out.println(s + x + у + z),



В данном случае компилятор Java приводит переменные х, у и z к их строко¬вому представлению, вместо того чтобы сначала арифметически сложить их. А если вы запишете



System.out.println(x + s); то и здесь Java преобразует х в строку.



Типичные ошибки при использовании операторов



Многие программисты склонны второпях записывать выражение без скобок, даже когда они не уверены в последовательности вычисления выражения. Это верно и для Java.



Еще одна распространенная ошибка в С и С++ выглядит следующим об¬разом:



while(x = у) { // .



}



Программист хотел выполнить сравнение (==), а не присвоение. В С и С++ результат этого выражения всегда будет истинным, если только у не окажется нулем; вероятно, возникнет бесконечный цикл. В языке Java результат такого выражения не будет являться логическим типом (boolean), а компилятор ожи¬дает в этом выражении именно boolean и не разрешает использовать целочис¬ленный тип int, поэтому вовремя сообщит вам об ошибке времени компиляции, упредив проблему еще перед запуском программы. Поэтому подобная ошибка в Java никогда не происходит. (Программа откомпилируется только в одном случае: если х и у одновременно являются типами boolean, и тогда выражение х = у будет допустимо, что может привести к ошибке.)



Похожая проблема возникает в С и С++ при использовании поразрядных операторов И и ИЛИ вместо их логических аналогов. Поразрядные И и ИЛИ записываются одним символом (& и |), в то время как логические И и ИЛИ тре¬буют в написании двух символов (&& и ||). Так же, как и в случае с операторами = и ==, легко ошибиться и набрать один символ вместо двух. В Java компилятор предотвращает такие ошибки, так как он не позволяет использовать тип данных в неподходящем контексте.



Операторы приведения



Слово приведение используется в смысле «приведение к другому типу». В опре¬деленных ситуациях Java самостоятельно преобразует данные к другим типам. Например, если вещественной переменной присваивается целое значение, ком¬пилятор автоматически выполняет соответствующее преобразование (int пре¬образуется во float). Приведение позволяет сделать замену типа более очевид¬ной или выполнить ее принудительно в случаях, где это не происходит в обыч¬ном порядке.



Чтобы выполнить приведение явно, запишите необходимый тип данных (включая все модификаторы) в круглых скобках слева от преобразуемого зна¬чения. Пример:



//: operators/Casting.java



public class Casting {



public static void main(String[] args) { int i = 200; long lng = (long)i;



Ing = i; // "Расширение", явное преобразование не обязательно long lng2 = (long)200; lng2 = 200;



// "Сужающее" преобразование i = (int)lng2; // Преобразование необходимо



}



} ///:-



Как видите, приведение может выполняться и для чисел, и для переменных. Впрочем, в указанных примерах приведение является излишним, поскольку компилятор при необходимости автоматически преобразует целое int к типу long. Однако это не мешает вам выполнять необязательные приведения — на¬пример, чтобы подчеркнуть какое-то обстоятельство или просто для того, что¬бы сделать программу более понятной. В других ситуациях приведение может быть необходимо для нормальной компиляции программы.



В С и С++ приведение могло стать источником ошибок и неоднозначности. В Java приведение безопасно, за одним исключением: при выполнении так на¬зываемого сужающего приведения (то есть от типа данных, способного хранить больше информации, к менее содержательному типу данных), то есть при опас¬ности потери данных. В таком случае компилятор заставляет вас выполнить яв¬ное приведение; фактически он говорит: «это может быть опасно, но, если вы уверены в своей правоте, опишите действие явно». В случае с расширяющим приведением явное описание не понадобится, так как новый тип данных спосо¬бен хранить больше информации, чем прежний, и поэтому потеря данных ис¬ключена.



В Java разрешается приводить любой простейший тип данных к любому другому простейшему типу, но это не относится к типу boolean, который вооб¬ще не подлежит приведению. Классы также не поддерживают произвольное приведение. Чтобы преобразовать один класс в другой, требуются специальные методы. (Как будет показано позднее, объекты можно преобразовывать в рам¬ках семейства типов; объект Дуб можно преобразовать в Дерево и наоборот, но не к постороннему типу вроде Камня.)



Округление и усечение



При выполнении сужающих преобразований необходимо обращать внимание на усечение и округление данных. Например, как должен действовать компиля¬тор Java при преобразовании вещественного числа в целое? Скажем, если зна¬чение 29,7 приводится к типу int, что получится — 29 или 30? Ответ на этот во¬прос может дать следующий пример:



//: operators/CastingNumbers.java // Что происходит при приведении типов // float или double к целочисленным значениям? import static net.mindview.util.Print *;



public class CastingNumbers {



public static void main(String[] args) { double above = 0.7, below = 0.4; float fabove = 0.7f, fbelow = 0 4f; print("(int)above: " + (int)above); print("(int)below: " + (int)below), printC(int)fabove- " + (int)fabove); print("(int)fbelow. " + (int)fbelow),



}



} /* Output: (int)above: 0 (int)below. 0 (int)fabove: 0 (int)fbelow: 0 *///:-



Отсюда и ответ на наш вопрос — приведение от типов с повышенной точно¬стью double и float к целочисленным значениям всегда осуществляется с усечени¬ем целой части. Если вы предпочитаете, чтобы результат округлялся, используйте метод round() из java.lang.Math. Так как этот метод является частью java.lang, до¬полнительное импортирование не потребуется.



Повышение



Вы можете обнаружить, что при проведении любых математических и пораз¬рядных операций примитивные типы данных, меньшие int (то есть char, byte и short), приводятся к типу int перед проведением операций, и получаемый ре¬зультат имеет тип int. Поэтому, если вам снова понадобится присвоить его меньшему типу, придется использовать приведение. (И тогда возможна потеря информации.) В основном самый емкий тип данных, присутствующий в выра¬жении, и определяет величину результата этого выражения; так, при перемно¬жении float и double результатом станет double, а при сложении long и int вы по¬лучите в результате long.



В Java отсутствует sizeof()



В С и С++ оператор sizeof() выдает количество байтов, выделенных для хране¬ния данных. Главная причина для использования sizeof() — переносимость про¬граммы. Различным типам данных может отводиться различное количество па¬мяти на разных компьютерах, поэтому для программиста важно определить размер этих типов перед проведением операций, зависящих от этих величин. Например, один компьютер выделяет под целые числа 32 бита, а другой — всего лишь 16 бит. В результате на первой машине программа может хранить в цело¬численном представлении числа из большего диапазона. Конечно, аппаратная совместимость создает немало хлодот для программистов на С и С++.



В Java оператор sizeof() не нужен, так как все типы данных имеют одинако¬вые размеры на всех машинах. Вам не нужно заботиться о переносимости на низком уровне — она встроена в язык.



Сводка операторов



Следующий пример показывает, какие примитивные типы данных используются с теми или иными операторами. Вообще-то это один и тот же пример, повторен¬ный много раз, но для разных типов данных. Файл должен компилироваться без ошибок, поскольку все строки, содержащие неверные операции, предварены символами //!.



// operators/AHOps java



// Проверяет все операторы со всеми



// примитивными типами данных, чтобы показать,



// какие операции допускаются компилятором Java



public class AT 1 Ops {



// для получения результатов тестов типа boolean: void f(boolean b) {} void boolTest(boolean x, boolean y) { // Арифметические операции- //' x = x * у; //! x = x / у;



//! х = х % у; III х = х + у; III х = х - у; III х++; //! х--; //! х = +у; //! х = -у;



// Операции сравнения и логические операции:



III f(х > у),



//! f(х >= у);



III f(х < у).



Ill f(х <= у);



f(x == у),



f(х != у);



f(!y);



х = х && у: х = х || у.



III х =



X = X =



II II II // // // // // // // // // X &= у: х А= у: х |= у.



// Приведение-



// Поразрядные операторы:



-у; & у.



у:



У:



х « 1; х » 1, х »> 1; Совмещенное присваивание: х += у: х у. *= У; /= у; := У. «= 1; »= 1. »>= 1;



II char с = (char)x,



II byte В = (byte)x:



II short s : = (short)x;



II int i = (int)x:



П long 1 = (long)x:



п float f ; = (float)x;



II double d = (double)x;









void charTest(char х, char у) {



х = х



х++, х- -,



X = X =



// Арифметические операции- х = (char)(x * у): х = (char)(x / у). х = (char)(x % у), (char)Сх + у), (char)(x - у),



(char)+y; (char)-y:






// Операции сравнения и логические операции:



f(x > у);



f(x >= у);



f(x < у);



f(x <= у);



f(x == у);



fCx != у);



//! f(!x);



//! f(x && у);



//! f(x || у).



// Поразрядные операции:



х= (charby;



х = (char)(x & у).



х = (char)(x | у):



х = (char)(x А у):



х = (char)(x « 1):



х = (char)(x » 1):



х = (char)(x »> 1):



// Совмещенное присваивание:



х += у,



х -= у:



х *= у;



х /= у:



х Х- у:



х «= 1.



X »= 1,



х »>= 1: х &= у; х А= у; х |= У.



// Приведение-



//! boolean b = (boolean)x:



byte В = (byte)x:



short s = (short)x;



int i = (int)x;



long 1 = (long)x:



float f = (float)x;



double d = (double)x,



}



void byteTest(byte x. byte y) {



// Арифметические операции- x = (byte)(x* у): x = (byte)(x / y): x = (byte)(x % y), x = (byte)Cx + y); x = (byte)(x - y), x++: x--:



x = (byte)+ y: x = (byte)- y:



// Операции сравнения и логические операции:



f(x > у);



f(x >= у):



f(x < у);



f(x <= у):



f(x == у):



f(x != у):



//! f(!x):



//! f(x && у),



//! f(x || у); // Поразрядные операции: х = (byte)-y; х = (byte)(х & у): х = (byte)(x | у); х = (byte)(x А у); х = (byte)(x « 1); х = (byte)(x » 1); х = (byte) (х »> 1). // Совмещенное присваивание: х += у: х -= у: х *= у: х /= у: х у. х «= 1; х »= 1, х »>= 1, х &= у; х у. х |= У,



// Приведение:



//! boolean b = (boolean)x,



char с = (char)x;



short s = (short)x;

Report Page