Программирование на Java
Шрифт:
void calculate(long a) {
...
}
void main {
calculate(new Long(5));
// здесь будет ошибка
}
Если сужение, то компилятор не сможет осуществить приведение и потребуются явные указания.
void calculate(int a) {
...
}
void main {
long a=5;
// calculate(a);
// сужение! так будет ошибка.
calculate((int)a);
// корректный вызов
}
Наконец, в случае расширения, компилятор
Надо отметить, что, в отличие от ситуации присвоения, при вызове методов компилятор не производит преобразований примитивных значений от byte, short, char или int к byte, short или char. Это привело бы к усложнению работы с перегруженными методами. Например:
// пример вызовет ошибку компиляции
// объявляем перегруженные методы
// с аргументами (byte, int) и (short, short)
int m(byte a, int b) { return a+b;}
int m(short a, short b) { return a-b;}
void main {
print(m(12, 2));
// ошибка компиляции!
}
В этом примере компилятор выдаст ошибку, так как при вызове аргументы имеют тип ( int, int ), а метода с такими параметрами нет. Если бы компилятор проводил преобразование для целых величин, подобно ситуации с присвоением значений, то пример стал бы корректным, но пришлось бы прилагать дополнительные усилия, чтобы указать, какой из двух возможных перегруженных методов хотелось бы вызвать.
Аналогичное преобразование потребуется при возвращении значения из метода, если тип результата и заявленный тип возвращаемого значения не совпадают.
long get {
return 5;
}
Хотя в выражении return указан целочисленный литерал типа int, во всех местах, где будет вызван этот метод, будет получено значение типа long. Для такого преобразования действуют те же правила, что и для присвоения значения.
В заключение рассмотрим пример, включающий в себя все рассмотренные случаи преобразования:
short get(Parent p) {
return 5+'A';
// приведение при возвращении значения
}
void main {
long a = 5L;
// приведение при присвоении значения
get(new Child);
// приведение при вызове метода
}
Явное приведение
Явное приведение уже многократно использовалось в примерах. При таком преобразовании слева от выражения, тип значения которого необходимо преобразовать, в круглых скобках указывается целевой тип. Если преобразование пройдет успешно, то результат будет точно указанного типа. Примеры:
(byte)5
(Parent)new Child
(Flat)getCity.getStreet(
).getHouse.getFlat
Если комбинация типов образует запрещенное преобразование, возникает ошибка компиляции. Допускаются тождественные преобразования, расширения простых и объектных типов, сужения простых и объектных типов. Первые три всегда выполняются успешно. Последние два могут стать причиной ошибки исполнения, если значения оказались несовместимыми.
Child c=new Child;
// Child2 c2=(Child2)c;
// запрещенное преобразование
Parent p=c;
// расширение
Child2 c2=(Child2)p;
// сужение
Такой код будет успешно скомпилирован, однако, разумеется, при исполнении он всегда будет генерировать ошибку в последней строке. "Обманывать" компилятор смысла нет.
Оператор конкатенации строк
Этот оператор уже рассматривался достаточно подробно. Если обоими его аргументами являются строки, то происходит обычная конкатенация. Если же тип String имеет лишь один из аргументов, то второй необходимо преобразовать в текст. Это единственная операция, при которой производится универсальное приведение любого значения к типу String.
Это одно из свойств, выделяющих класс String из общего ряда.
Правила преобразования уже были подробно описаны в этой лекции, а оператор конкатенации рассматривался в лекции "Типы данных".
Небольшой пример:
int i=1;
double d=i/2.;
String s="text";
print("i="+i+", d="+d+", s="+s");
Результатом будет:
i=1, d=0.5, s=text
Числовое расширение
Наконец, последний вид преобразований применяется при числовых операциях, когда требуется привести аргумент(ы) к типу длиной в 32 или 64 бита для проведения вычислений. Таким образом, при числовом расширении осуществляется только расширение примитивных типов.
Различают унарное и бинарное числовое расширение.
Унарное числовое расширение
Это преобразование расширяет примитивные типы byte, short или char до типов int по правилам расширения примитивных типов.
Унарное числовое расширение может выполняться при следующих операциях:
* унарные операции + и - ;
* битовое отрицание ~ ;
* операции битового сдвига <<, >>, >>>.
Операторы сдвига имеют два аргумента, но они расширяются независимо друг от друга, поэтому данное преобразование является унарным. Таким образом, результат выражения 5<<3L имеет тип int. Вообще, результат операторов сдвига всегда имеет тип int или long.
Примеры работы всех этих операторов с учетом расширения подробно рассматривались в предыдущих лекциях.
Бинарное числовое расширение
Это преобразование расширяет все примитивные числовые типы, кроме double, до типов int, long, float, double по правилам расширения примитивных типов. Бинарное числовое расширение происходит при числовых операторах, имеющих два аргумента, по следующим правилам:
* если любой из аргументов имеет тип double, то и второй приводится к double ;