Spec-Zone .ru
спецификации, руководства, описания, API
|
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Второй Выпуск |
ГЛАВА 5
У каждого выражения, записанного в языке программирования Java, есть тип, который может быть выведен из структуры выражения и типов литералов, переменных, и методов, упомянутых в выражении. Возможно, однако, записать выражение в контексте, где тип выражения не является соответствующим. В некоторых случаях это приводит к ошибке во время компиляции; например, если выражение в if
у оператора (§14.9) есть любой тип кроме boolean
, ошибка времени компиляции происходит. В других случаях контекст может быть в состоянии принять тип, который связывается с типом выражения; как удобство, вместо того, чтобы требовать, чтобы программист указал на преобразование типов явно, язык выполняет неявное преобразование из типа выражения к типу, приемлемому для его окружающего контекста.
Определенное преобразование от типа S до типа T позволяет выражению типа S быть обработанным во время компиляции, как будто у этого был тип T вместо этого. В некоторых случаях это потребует, чтобы соответствующее действие во время выполнения проверило законность преобразования или преобразовало значение времени выполнения выражения в форму, подходящую для нового типа T. Например:
Object
вводить Thread
требует, чтобы проверка на этапе выполнения удостоверилась, что значение времени выполнения является фактически экземпляром класса Thread
или один из его подклассов; если это не, исключение выдается.
Thread
вводить Object
не требует никакого действия времени выполнения; Thread
подкласс Object
, таким образом, любая ссылка производится выражением типа Thread
допустимое ссылочное значение типа Object
.
int
вводить long
требует расширения знака времени выполнения 32-разрядного целочисленного значения к 64-разрядному long
представление. Никакая информация не теряется.
double
вводить long
требует нетривиального преобразования от 64-разрядного значения с плавающей точкой до 64-разрядного целочисленного представления. В зависимости от фактического значения времени выполнения может быть потеряна информация. В каждом контексте преобразования только разрешаются определенные определенные преобразования. Для удобства описания определенные преобразования, которые возможны в языке программирования Java, группируются в несколько широких категорий:
Один контекст преобразования является операндом числового оператора такой как +
или *
. Процесс преобразования для таких операндов вызывают числовым продвижением. Продвижение является особенным в этом, в случае бинарных операторов, преобразование, выбранное для одного операнда, может зависеть частично от типа другого выражения операнда. Эта глава сначала описывает семь категорий преобразований (§5.1), включая специальные преобразования в String
учтенный оператор конкатенации строк +
. Затем пять контекстов преобразования описываются:
String
.
который производит вывод:class Test { public static void main(String[] args) { // Casting conversion (§5.4) of a float literal to // type int. Without the cast operator, this would // be a compile-time error, because this is a // narrowing conversion (§5.1.3): int i = (int)12.5f; // String conversion (§5.4) of i's int value: System.out.println("(int)12.5f==" + i); // Assignment conversion (§5.2) of i's value to type // float. This is a widening conversion (§5.1.2): float f = i; // String conversion of f's float value: System.out.println("after float widening: " + f); // Numeric promotion (§5.6) of i's value to type // float. This is a binary numeric promotion. // After promotion, the operation is float*float: System.out.print(f); f = f * i; // Two string conversions of i and f: System.out.println("*" + i + "==" + f); // Method invocation conversion (§5.3) of f's value // to type double, needed because the method Math.sin // accepts only a double argument: double d = Math.sin(f); // Two string conversions of f and d: System.out.println("Math.sin(" + f + ")==" + d); } }
(int)12.5f==12 after float widening: 12.0 12.0*12==144.0 Math.sin(144.0)==-0.49102159389846934
Это может казаться тривиальным, но у этого есть два практических последствия. Во-первых, всегда разрешается для выражения иметь требуемый тип для начала, таким образом позволяя просто установленное правило, что каждое выражение подвергается преобразованию, если только тривиальное преобразование идентификационных данных. Во-вторых, это подразумевает, что разрешается для программы включать избыточные операторы броска ради ясности.
Единственное разрешенное преобразование, которое включает тип boolean
преобразование идентификационных данных из boolean
к boolean
.
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
float
к double
не теряйте информацию вообще; числовое значение сохраняется точно. Преобразования, расширяющиеся от float
к double
в strictfp
выражения также сохраняют числовое значение точно; однако, такие преобразования, которые не являются strictfp
может потерять информацию о полной величине преобразованного значения.
Преобразование int
или a long
значение к float
, или a long
значение к double
, может привести к потере точности - то есть, результат может потерять некоторые из младших значащих битов значения. В этом случае получающееся значение с плавающей точкой будет правильно округленной версией целочисленного значения, используя режим раунда-к-самому-близкому IEEE 754 (§4.2.4).
Расширяющееся преобразование целого числа со знаком оценивает целочисленному типу T, просто подписываются - расширяет two's-дополнительное представление целочисленного значения, чтобы заполнить более широкий формат. Расширяющееся преобразование символа к целочисленному типу T нуль - расширяет представление символьного значения, чтобы заполнить более широкий формат.
Несмотря на то, что потеря точности может произойти, расширяя преобразования среди типов примитивов никогда не приводит к исключению на этапе выполнения (§11).
Вот пример расширяющегося преобразования, которое теряет точность:
который печатает:class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }
таким образом указание, что информация была потеряна во время преобразования из типа-46
int
вводить float
потому что значения типа float
не точны к девяти существенным цифрам.
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
Сужающееся преобразование целого числа со знаком к целочисленному типу T просто отбрасывает все кроме n битов самых низкоуровневых, где n является числом битов, используемых, чтобы представить тип T. В дополнение к возможной потере информации о величине числового значения это может вызвать знак получающегося значения отличаться от знака входного значения.
Сужающееся преобразование символа к целочисленному типу T аналогично просто отбрасывает все кроме n битов самых низкоуровневых, где n является числом битов, используемых, чтобы представить тип T. В дополнение к возможной потере информации о величине числового значения это может заставить получающееся значение быть отрицательным числом, даже при том, что символы представляют 16-разрядные значения целого без знака.
Сужающееся преобразование числа с плавающей точкой к целочисленному типу T делает два шага:
long
, если T long
, или к int
, если T byte
, short
, char
, или int
, следующим образом: int
или long
0
.
long
, и это целочисленное значение может быть представлено как a long
, тогда результат первого шага long
значение V.
int
, тогда результат первого шага int
значение V. int
или long
.
int
или long
. int
или long
, результатом преобразования является результат первого шага.
byte
, char
, или short
, результатом преобразования является результат сужающегося преобразования в тип T (§5.1.3) результата первого шага.
производит вывод:class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } }
Результаты дляlong: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1
char
, int
, и long
неудивительны, производя минимальные и максимальные представимые значения типа.
Результаты для byte
и short
потеряйте информацию о знаке и величине числовых значений и также потеряйте точность. Результаты могут быть поняты, исследуя биты младшего разряда минимума и максимума int.
Минимум int
в шестнадцатеричном, 0x80000000
, и максимум int
0x7fffffff
. Это объясняет short
результаты, которые составляют низкие 16 битов этих значений, а именно, 0x0000
и 0xffff
; это объясняет char
результаты, которые также составляют низкие 16 битов этих значений, а именно, '\u0000'
и '\uffff'
; и это объясняет byte
результаты, которые составляют низкие 8 битов этих значений, а именно, 0x00
и 0xff
. Несмотря на то, что переполнение, потеря значимости, или другая потеря информации могут произойти, сужая преобразования среди типов примитивов никогда не приводит к исключению на этапе выполнения (§11).
Вот маленькая тестовая программа, которая демонстрирует много сужающихся преобразований, которые теряют информацию:
Эта тестовая программа производит следующий вывод:class Test { public static void main(String[] args) { // A narrowing of int to short loses high bits: System.out.println("(short)0x12345678==0x" + Integer.toHexString((short)0x12345678)); // A int value not fitting in byte changes sign and magnitude: System.out.println("(byte)255==" + (byte)255); // A float value too big to fit gives largest int value: System.out.println("(int)1e20f==" + (int)1e20f); // A NaN converted to int yields zero: System.out.println("(int)NaN==" + (int)Float.NaN); // A double value too large for float yields infinity: System.out.println("(float)-1e100==" + (float)-1e100); // A double value too small for float underflows to zero: System.out.println("(float)1e-50==" + (float)1e-50); } }
(short)0x12345678==0x5678 (byte)255==-1 (int)1e20f==2147483647 (int)NaN==0 (float)-1e100==-Infinity (float)1e-50==0.0
Object
от любого другого типа класса.)
Object
.
Object
.
Cloneable
.
java.io.Serializable
[]
любому массиву вводят TC[]
, при условии, что SC и TC являются ссылочными типами и есть расширяющееся преобразование от SC до TC. См. §8 для подробных спецификаций для классов, §9 для интерфейсов, и §10 для массивов.
Object
к любому другому типу класса.)
Object
к любому интерфейсному типу.)
Object
к любому типу массива.
Object
к любому интерфейсному типу.
final
.
final
, при условии, что T реализует J.
[]
любому массиву вводят TC[]
, при условии, что SC и TC являются ссылочными типами и есть сужающееся преобразование от SC до TC. ClassCastException
бросается.String
от любого типа, включая нулевой тип.boolean
кроме преобразования идентификационных данных.
boolean
кроме преобразования идентификационных данных и преобразования строк.
final
и не реализует K.
Object
.
final
и не реализует J.
Object
или String
.
java.io.Serializable
и Cloneable
, которые реализуются всеми массивами.
[]
выстраивать TC типа[]
если нет никакого разрешенного преобразования кроме преобразования строк от SC до TC.
В пределах выражения, которое не строго FP (§15.4), преобразование набора значений обеспечивает варианты для реализации языка программирования Java:
float
и не элемент набора значений плавающего, тогда реализация должна отобразить значение на самый близкий элемент набора значений плавающего. Это преобразование может привести к переполнению или потере значимости.
double
и не элемент двойного набора значений, тогда реализация должна отобразить значение на самый близкий элемент двойного набора значений. Это преобразование может привести к переполнению или потере значимости.
Ли в строгом FP коде или коде, который не строг FP, преобразование набора значений всегда листы, неизменные любое значение, тип которого ни один не float
ни double
.
byte
, short
, char
или int
.
byte
, short
, или char
.
Если тип переменной float
или double
, тогда преобразование набора значений применяется после преобразования типов:
float
и элемент набора значений "расширенная экспонента плавающая", тогда реализация должна отобразить значение на самый близкий элемент набора значений плавающего. Это преобразование может привести к переполнению или потере значимости.
double
и элемент набора значений "двойная расширенная экспонента", тогда реализация должна отобразить значение на самый близкий элемент двойного набора значений. Это преобразование может привести к переполнению или потере значимости. Преобразование присвоения никогда не вызывает исключение. (Отметьте, однако, что присвоение может привести к исключению в массиве включения особого случая, элементы - видят §10.10 и §15.26.1.)
Время компиляции, сужаясь констант означает что код, такой как:
позволяется. Без сужения, факт, что целочисленный литералbyte theAnswer = 42;
42
имеет тип int
означал бы что бросок для byte
требовался бы:
Значение типа примитива не должно быть присвоено переменной ссылочного типа; попытка сделать так приведет к ошибке времени компиляции. Значение типаbyte theAnswer = (byte)42; // cast is permitted but not required
boolean
может быть присвоен только переменной типа boolean
.Следующая тестовая программа содержит примеры преобразования присвоения примитивных значений:
Это производит следующий вывод:class Test { public static void main(String[] args) { short s = 12; // narrow 12 to short float f = s; // widen short to float System.out.println("f=" + f); char c = '\u0123'; long l = c; // widen char to long System.out.println("l=0x" + Long.toString(l,16)); f = 1.23f; double d = f; // widen float to double System.out.println("d=" + d); } }
Следующий тест, однако, производит ошибки времени компиляции:f=12.0 l=0x123 d=1.2300000190734863
потому что не всеclass Test { public static void main(String[] args) { short s = 123; char c = s; // error: would require cast s = c; // error: would require cast } }
short
значения char
значения, и ни один не все char
значения short
значения.Значение нулевого типа (нулевая ссылка является единственным такое значение) может быть присвоено любому ссылочному типу, приводящему к нулевой ссылке того типа.
Вот пример программы, иллюстрирующий присвоения ссылок:
Присвоение значения ссылочного типа времени компиляции S (источник) к переменной ссылочного типа времени компиляции T (цель) проверяется следующим образом:public class Point { int x, y; } public class Point3D extends Point { int z; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { // Assignments to variables of class type: Point p = new Point(); p = new Point3D(); // ok: because Point3D is a // subclass of Point Point3D p3d = p; // error: will require a cast because a // Point might not be a Point3D // (even though it is, dynamically, // in this example.) // Assignments to variables of type Object: Object o = p; // ok: any object to Object int[] a = new int[3]; Object o2 = a; // ok: an array to Object // Assignments to variables of interface type: ColoredPoint cp = new ColoredPoint(); Colorable c = cp; // ok: ColoredPoint implements // Colorable // Assignments to variables of array type: byte[] b = new byte[4]; a = b; // error: these are not arrays // of the same primitive type Point3D[] p3da = new Point3D[3]; Point[] pa = p3da; // ok: since we can assign a // Point3D to a Point p3da = pa; // error: (cast needed) since a Point // can't be assigned to a Point3D } }
Object
, или ошибка времени компиляции происходит.
[]
, то есть, массив компонентов SC типа: Object
, или ошибка времени компиляции происходит.
java.io.Serializable
или тип Cloneable
, единственные интерфейсы реализуются массивами.
[]
, то есть, массив компонентов TC типа, затем ошибка времени компиляции происходит, если одно из следующего не является истиной:
Следующая тестовая программа иллюстрирует преобразования присвоения на ссылочных значениях, но не в состоянии скомпилировать, потому что она нарушает предыдущие правила, как описано в его комментариях. Этот пример должен быть по сравнению с предыдущим.
Вот другое присвоение включения в качестве примера объектов массива:public class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); // Okay because ColoredPoint is a subclass of Point: p = cp; // Okay because ColoredPoint implements Colorable: Colorable c = cp; // The following cause compile-time errors because // we cannot be sure they will succeed, depending on // the run-time type of p; a run-time check will be // necessary for the needed narrowing conversion and // must be indicated by including a cast: cp = p; // p might be neither a ColoredPoint // nor a subclass of ColoredPoint c = p; // p might not implement Colorable } }
В этом примере:class Point { int x, y; } class ColoredPoint extends Point { int color; } class Test { public static void main(String[] args) { long[] veclong = new long[100]; Object o = veclong; // okay Long l = veclong; // compile-time error short[] vecshort = veclong; // compile-time error Point[] pvec = new Point[100]; ColoredPoint[] cpvec = new ColoredPoint[100]; pvec = cpvec; // okay pvec[0] = new Point(); // okay at compile time, // but would throw an // exception at run time cpvec = pvec; // compile-time error } }
veclong
не может быть присвоен a Long
переменная, потому что Long
тип класса кроме Object
. Массив может быть присвоен только переменной совместимого типа массива, или к переменной типа Object
.
veclong
не может быть присвоен vecshort
, потому что они - массивы типа примитива, и short
и long
не тот же самый тип примитива.
cpvec
может быть присвоен pvec
,
потому что любая ссылка, которая могла быть значением выражения типа ColoredPoint
может быть значение переменной типа Point
. Последующее присвоение нового Point
к компоненту pvec
тогда бросил бы ArrayStoreException
(если программа была иначе исправлена так, чтобы она могла быть скомпилирована), потому что a ColoredPoint
у массива не может быть экземпляра Point
как значение компонента.
pvec
не может быть присвоен cpvec
,
потому что не каждая ссылка, которая могла быть значением выражения типа ColoredPoint
может правильно быть значение переменной типа Point
. Если значение pvec
во время выполнения была ссылка на экземпляр Point[]
, и присвоение на cpvec
были позволены, простая ссылка на компонент cpvec
, Скажем, cpvec[0]
, мог возвратить a Point
, и a Point
не a ColoredPoint
. Таким образом позволить такое присвоение позволило бы нарушение системы типов. Бросок может использоваться (§5.5, §15.16), чтобы гарантировать это pvec
ссылки a ColoredPoint[]
: cpvec = (ColoredPoint[])pvec; // okay, but may throw an // exception at run time
Если тип выражения параметра также float
или double
, тогда преобразование набора значений (§5.1.8) применяется после преобразования типов:
float
элемент набора значений "расширенная экспонента плавающая", тогда реализация должна отобразить значение на самый близкий элемент набора значений плавающего. Это преобразование может привести к переполнению или потере значимости.
double
элемент набора значений "двойная расширенная экспонента", тогда реализация должна отобразить значение на самый близкий элемент двойного набора значений. Это преобразование может привести к переполнению или потере значимости.
вызывает ошибку времени компиляции потому что целочисленные литералыclass Test { static int m(byte a, int b) { return a+b; } static int m(short a, short b) { return a-b; } public static void main(String[] args) { System.out.println(m(12, 2)); // compile-time error } }
12
и 2
имейте тип int
, так никакой метод m
соответствия по правилам (§15.12.2). Язык, который включал неявное сужение целочисленных констант, будет нуждаться в дополнительных правилах разрешить случаи как этот пример.+
оператор, когда одним из параметров является a String
. В этом единственном особом случае, другом параметре +
преобразовывается в a String
, и новое String
то, который является связью двух строк, является результатом +
. Преобразование строк определяется подробно в пределах описания конкатенации строк +
оператор (§15.18.1).Преобразование набора значений (§5.1.8) применяется после преобразования типов.
Некоторые броски могут быть доказаны неправильными во время компиляции; такие броски приводят к ошибке времени компиляции.
Значение типа примитива может быть брошено к другому типу примитива преобразованием идентификационных данных, если типы являются тем же самым, или расширяющимся примитивным преобразованием или сужающимся примитивным преобразованием.
Значение типа примитива не может быть брошено к ссылочному типу, бросая преобразование, ни может значение ссылочного типа быть брошенным к типу примитива.
Остающиеся случаи включают преобразование между ссылочными типами. Подробные правила для проверки правильности времени компиляции преобразования кастинга значения ссылочного типа времени компиляции S (источник) к ссылочному типу времени компиляции T (цель) следующие:
final
класс (§8.1.1), тогда бросок всегда корректен во время компиляции (потому что, даже если S не реализует T, подкласс S мог бы).
final
класс (§8.1.1), тогда S должен реализовать T, или ошибка времени компиляции происходит. Object
, или ошибка времени компиляции происходит. final
(§8.1.1), тогда бросок всегда корректен во время компиляции (потому что, даже если T не реализует S, подкласс T мог бы).
[]
, то есть, массив компонентов SC типа: Object
, тогда ошибка времени компиляции происходит (потому что Object
единственный тип класса, которому массивы могут быть присвоены).
java.io.Serializable
или тип Cloneable
, единственные интерфейсы реализуются массивами.
[]
, то есть, массив компонентов TC типа, затем ошибка времени компиляции происходит, если одно из следующего не является истиной:
Если бросок к ссылочному типу не является ошибкой времени компиляции, есть два случая:
null
, тогда бросок позволяется. Иначе, позвольте R быть классом объекта, упомянутого ссылочным значением времени выполнения, и позволять T быть типом, названным в операторе броска. Преобразование броска должно проверить во время выполнения, что класс R является присвоением, совместимым с типом T, используя алгоритм, определенный в §5.2, но используя класс R вместо типа S времени компиляции как определено там. (Отметьте, что R не может быть интерфейсом, когда эти правила сначала применяются для любого данного броска, но R может быть интерфейсом, если правила применяются рекурсивно, потому что ссылочное значение времени выполнения может обратиться к массиву, тип элемента которого является интерфейсным типом.) Измененный алгоритм показывают здесь: Object
(§4.3.2), или исключение на этапе выполнения бросается.
[]
- то есть, массив компонентов RC типа: Object
(§4.3.2), или исключение на этапе выполнения бросается.
java.io.Serializable
или тип Cloneable
, единственные интерфейсы, реализованные массивами (этот случай мог уменьшиться мимо времени компиляции, проверяя, была ли, например, ссылка на массив сохранена в переменной типа Object
).
[]
, то есть, массив компонентов TC типа, затем исключение на этапе выполнения бросается, если одно из следующего не является истиной:
ClassCastException
.Вот некоторые примеры кастинга преобразований ссылочных типов, подобных примеру в §5.2:
Здесь первая ошибка времени компиляции происходит потому что типы классовpublic class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } final class EndPoint extends Point { } class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); Colorable c; // The following may cause errors at run time because // we cannot be sure they will succeed; this possibility // is suggested by the casts: cp = (ColoredPoint)p; // p might not reference an // object which is a ColoredPoint // or a subclass of ColoredPoint c = (Colorable)p; // p might not be Colorable // The following are incorrect at compile time because // they can never succeed as explained in the text: Long l = (Long)p; // compile-time error #1 EndPoint e = new EndPoint(); c = (Colorable)e; // compile-time error #2 } }
Long
и Point
не связаны (то есть, они не то же самое, и ни один не подкласс другого), таким образом, бросок между ними всегда перестанет работать.
Вторая ошибка времени компиляции происходит потому что переменная типа EndPoint
никогда не может ссылаться на значение, которое реализует интерфейс Colorable
. Это то, потому что EndPoint
a final
введите, и переменная a final
тип всегда содержит значение того же самого типа времени выполнения как его тип времени компиляции. Поэтому, тип времени выполнения переменной e
должен быть точно тип EndPoint
, и введите EndPoint
не реализует Colorable
.
Вот является включение в качестве примера массивами (§10):
Этот пример компилирует без ошибок и производит вывод:class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } public String toString() { return "("+x+","+y+")"; } } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; ColoredPoint(int x, int y, int color) { super(x, y); setColor(color); } public void setColor(int color) { this.color = color; } public String toString() { return super.toString() + "@" + color; } } class Test { public static void main(String[] args) { Point[] pa = new ColoredPoint[4]; pa[0] = new ColoredPoint(2, 2, 12); pa[1] = new ColoredPoint(4, 5, 24); ColoredPoint[] cpa = (ColoredPoint[])pa; System.out.print("cpa: {"); for (int i = 0; i < cpa.length; i++) System.out.print((i == 0 ? " " : ", ") + cpa[i]); System.out.println(" }"); } }
Следующий пример использует броски, чтобы скомпилировать, но он выдает исключения во время выполнения, потому что типы являются несовместимыми:cpa: { (2,2)@12, (4,5)@24, null, null }
public class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { Point[] pa = new Point[100]; // The following line will throw a ClassCastException: ColoredPoint[] cpa = (ColoredPoint[])pa; System.out.println(cpa[0]); int[] shortvec = new int[2]; Object o = shortvec; // The following line will throw a ClassCastException: Colorable c = (Colorable)o; c.setColor(0); } }
Числовые продвижения используются, чтобы преобразовать операнды числового оператора к общему типу так, чтобы работа могла быть выполнена. Два вида числового продвижения являются унарным числовым продвижением (§5.6.1) и двоичным числовым продвижением (§5.6.2). Аналогичные преобразования в C вызывают "обычными унарными преобразованиями" и "обычными двоичными преобразованиями."
Числовое продвижение не является общей функцией языка программирования Java, а скорее свойством определенных определений встроенных операций.
byte
, short
, или char
, унарное числовое продвижение продвигает это значение типа int
расширяющимся преобразованием (§5.1.2).
Унарное числовое продвижение выполняется по выражениям в следующих ситуациях:
+
(§15.15.3)
-
(§15.15.4)
~
(§15.15.5)
>>
, >>>
, или <<
(§15.19); поэтому a long
расстояние сдвига (правильный операнд) не продвигает смещаемое значение (оставленный операнд) к long
Вот тестовая программа, которая включает примеры унарного числового продвижения:
Эта тестовая программа производит вывод:class Test { public static void main(String[] args) { byte b = 2; int a[] = new int[b]; // dimension expression promotion char c = '\u0001'; a[c] = 1; // index expression promotion a[0] = -c; // unary - promotion System.out.println("a: " + a[0] + "," + a[1]); b = -1; int i = ~b; // bitwise complement promotion System.out.println("~0x" + Integer.toHexString(b) + "==0x" + Integer.toHexString(i)); i = b << 4L; // shift promotion (left operand) System.out.println("0x" + Integer.toHexString(b) + "<<4L==0x" + Integer.toHexString(i)); } }
a: -1,1 ~0xffffffff==0x0 0xffffffff<<4L==0xfffffff0
double
, другой преобразовывается в double
.
float
, другой преобразовывается в float
.
long
, другой преобразовывается в long
.
int
. Двоичное числовое продвижение выполняется на операндах определенных операторов:
*
, /
и %
(§15.17)
+
и -
(§15.18.2)
<
, <=
, >
, и >=
(§15.20.1)
==
и !=
(§15.21.1)
&
, ^
, и |
(§15.22.1)
? :
(§15.25)
Пример двоичного числового продвижения появляется выше в §5.1. Вот другой:
который производит вывод:class Test { public static void main(String[] args) { int i = 0; float f = 1.0f; double d = 2.0; // First int*float is promoted to float*float, then // float==double is promoted to double==double: if (i * f == d) System.out.println("oops"); // A char&byte is promoted to int&int: byte b = 0x1f; char c = 'G'; int control = c & b; System.out.println(Integer.toHexString(control)); // Here int:float is promoted to float:float: f = (b==0) ? i : 4.0f; System.out.println(1.0/f); } }
Пример преобразовывает символ ASCII7 0.25
G
к г управления ASCII (BEL), маскируя от всех кроме низких 5 битов символа. 7
числовое значение этого управляющего символа.
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Второй Выпуск |