Spec-Zone .ru
спецификации, руководства, описания, API
Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск


ГЛАВА 5

Преобразования и Продвижения


У каждого выражения, записанного в языке программирования Java, есть тип, который может быть выведен из структуры выражения и типов литералов, переменных, и методов, упомянутых в выражении. Возможно, однако, записать выражение в контексте, где тип выражения не является соответствующим. В некоторых случаях это приводит к ошибке во время компиляции; например, если выражение в if у оператора (§14.9) есть любой тип кроме boolean, ошибка времени компиляции происходит. В других случаях контекст может быть в состоянии принять тип, который связывается с типом выражения; как удобство, вместо того, чтобы требовать, чтобы программист указал на преобразование типов явно, язык выполняет неявное преобразование из типа выражения к типу, приемлемому для его окружающего контекста.

Определенное преобразование от типа S до типа T позволяет выражению типа S быть обработанным во время компиляции, как будто у этого был тип T вместо этого. В некоторых случаях это потребует, чтобы соответствующее действие во время выполнения проверило законность преобразования или преобразовало значение времени выполнения выражения в форму, подходящую для нового типа T. Например:

Преобразование из типа double вводить long требует нетривиального преобразования от 64-разрядного значения с плавающей точкой до 64-разрядного целочисленного представления. В зависимости от фактического значения времени выполнения может быть потеряна информация.

В каждом контексте преобразования только разрешаются определенные определенные преобразования. Для удобства описания определенные преобразования, которые возможны в языке программирования Java, группируются в несколько широких категорий:

Есть пять контекстов преобразования, в которых может произойти преобразование выражений. Каждый контекст позволяет преобразования в некоторых из ранее названных категорий, но не другие. Термин "преобразование" также используется, чтобы описать процесс выбора определенного преобразования для такого контекста. Например, мы говорим, что выражение, которое является фактическим параметром в вызове метода, подвергается "преобразованию вызова метода," означая, что определенное преобразование будет неявно выбрано для того выражения согласно правилам для контекста параметра вызова метода.

Один контекст преобразования является операндом числового оператора такой как + или *. Процесс преобразования для таких операндов вызывают числовым продвижением. Продвижение является особенным в этом, в случае бинарных операторов, преобразование, выбранное для одного операнда, может зависеть частично от типа другого выражения операнда. Эта глава сначала описывает семь категорий преобразований (§5.1), включая специальные преобразования в 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

5.1 Виды Преобразования

Преобразования определенного типа в языке программирования Java делятся на семь категорий.

5.1.1 Преобразования идентификационных данных

Преобразование от типа до того же самого типа разрешается для любого типа.

Это может казаться тривиальным, но у этого есть два практических последствия. Во-первых, всегда разрешается для выражения иметь требуемый тип для начала, таким образом позволяя просто установленное правило, что каждое выражение подвергается преобразованию, если только тривиальное преобразование идентификационных данных. Во-вторых, это подразумевает, что разрешается для программы включать избыточные операторы броска ради ясности.

Единственное разрешенное преобразование, которое включает тип boolean преобразование идентификационных данных из boolean к boolean.

5.1.2 Расширение Примитивного Преобразования

Следующие 19 определенных преобразований на типах примитивов вызывают расширяющимися примитивными преобразованиями:

Расширяющиеся примитивные преобразования не теряют информацию о полной величине числового значения. Действительно, преобразования, расширяющиеся от целочисленного типа до другого целочисленного типа и от 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 не точны к девяти существенным цифрам.

5.1.3 Сужение Примитивных Преобразований

Следующие 23 определенных преобразования на типах примитивов вызывают сужающимися примитивными преобразованиями:

Сужение преобразований может потерять информацию о полной величине числового значения и может также потерять точность.

Сужающееся преобразование целого числа со знаком к целочисленному типу T просто отбрасывает все кроме n битов самых низкоуровневых, где n является числом битов, используемых, чтобы представить тип T. В дополнение к возможной потере информации о величине числового значения это может вызвать знак получающегося значения отличаться от знака входного значения.

Сужающееся преобразование символа к целочисленному типу T аналогично просто отбрасывает все кроме n битов самых низкоуровневых, где n является числом битов, используемых, чтобы представить тип T. В дополнение к возможной потере информации о величине числового значения это может заставить получающееся значение быть отрицательным числом, даже при том, что символы представляют 16-разрядные значения целого без знака.

Сужающееся преобразование числа с плавающей точкой к целочисленному типу T делает два шага:

  1. В первом шаге число с плавающей точкой преобразовывается любой в a long, если T long, или к int, если T byte, short, char, или int, следующим образом:
  2. Во втором шаге:
Пример:

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

5.1.4 Расширение Ссылочных Преобразований

Следующие преобразования вызывают расширяющимися ссылочными преобразованиями:

Такие преобразования никогда не требуют специального действия во время выполнения и поэтому никогда не выдают исключение во время выполнения. Они состоят просто в оценке ссылки как имеющий, некоторый другой вводит способ, который может быть доказан корректным во время компиляции.

См. §8 для подробных спецификаций для классов, §9 для интерфейсов, и §10 для массивов.

5.1.5 Сужение Ссылочных Преобразований

Следующие преобразования вызывают сужающимися ссылочными преобразованиями:

Такие преобразования требуют, чтобы тест во время выполнения узнал, является ли фактическое ссылочное значение законным значением нового типа. В противном случае тогда a ClassCastException бросается.

5.1.6 Преобразования строк

Есть преобразование строк, чтобы ввести String от любого типа, включая нулевой тип.

5.1.7 Запрещенный Преобразования

5.1.8 Преобразование Набора значений

Преобразование набора значений является процессом отображения значения с плавающей точкой от одного набора значений до другого, не изменяя его тип.

В пределах выражения, которое не строго FP (§15.4), преобразование набора значений обеспечивает варианты для реализации языка программирования Java:

В пределах строгого FP выражения (§15.4), преобразование набора значений не обеспечивает вариантов; каждая реализация должна вести себя таким же образом:

В пределах строгого FP выражения отображая значения от набора значений "расширенная экспонента плавающая" или набор значений "двойная расширенная экспонента" необходимы только, когда метод вызывается, чье объявление не строго FP, и реализация хотела представлять результат вызова метода как элемент набора значений расширенной экспоненты.

Ли в строгом FP коде или коде, который не строг FP, преобразование набора значений всегда листы, неизменные любое значение, тип которого ни один не float ни double.

5.2 Преобразование присвоения

Преобразование присвоения происходит, когда значение выражения присваивается (§15.26) переменной: тип выражения должен быть преобразован в тип переменной. Контексты присвоения позволяют использование преобразования идентификационных данных (§5.1.1), расширяющееся примитивное преобразование (§5.1.2), или расширяющееся ссылочное преобразование (§5.1.4). Кроме того, сужающееся примитивное преобразование может использоваться, если все следующие условия удовлетворяются:

Если тип выражения не может быть преобразован в тип переменной преобразованием, разрешенным в контексте присвоения, то ошибка времени компиляции происходит.

Если тип переменной 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 значения.

Значение нулевого типа (нулевая ссылка является единственным такое значение) может быть присвоено любому ссылочному типу, приводящему к нулевой ссылке того типа.

Вот пример программы, иллюстрирующий присвоения ссылок:

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
	}

}

Присвоение значения ссылочного типа времени компиляции S (источник) к переменной ссылочного типа времени компиляции T (цель) проверяется следующим образом:

См. §8 для спецификации классов, §9 для интерфейсов, и §10 для массивов.

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

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
	}
}
В этом примере:

cpvec = (ColoredPoint[])pvec;		// okay, but may throw an
					// exception at run time

5.3 Преобразование Вызова метода

Преобразование вызова метода применяется к каждому значению аргумента в методе или вызове конструктора (§15.9, §15.12): тип выражения параметра должен быть преобразован в тип соответствующего параметра. Контексты вызова метода позволяют использование преобразования идентификационных данных (§5.1.1), расширяющееся примитивное преобразование (§5.1.2), или расширяющееся ссылочное преобразование (§5.1.4).

Если тип выражения параметра также float или double, тогда преобразование набора значений (§5.1.8) применяется после преобразования типов:

Преобразования вызова метода определенно не включают неявное сужение целочисленных констант, которое является частью преобразования присвоения (§5.2). Разработчики языка программирования Java, который чувствуют, который включая эти неявные преобразования сужения добавил бы дополнительную сложность к перегруженному методу, соответствующему процесс разрешения (§15.12.2). Таким образом, пример:
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). Язык, который включал неявное сужение целочисленных констант, будет нуждаться в дополнительных правилах разрешить случаи как этот пример.

5.4 Преобразование строк

Преобразование строк применяется только к операндам двоичного файла + оператор, когда одним из параметров является a String. В этом единственном особом случае, другом параметре + преобразовывается в a String, и новое String то, который является связью двух строк, является результатом +. Преобразование строк определяется подробно в пределах описания конкатенации строк + оператор (§15.18.1).

5.5 Кастинг Преобразования

Кастинг преобразования применяется к операнду оператора броска (§15.16): тип выражения операнда должен быть преобразован в тип, явно названный оператором броска. Контексты кастинга позволяют использование преобразования идентификационных данных (§5.1.1), расширяющееся примитивное преобразование (§5.1.2), сужающееся примитивное преобразование (§5.1.3), расширяющееся ссылочное преобразование (§5.1.4), или сужающееся ссылочное преобразование (§5.1.5). Таким образом преобразования кастинга являются более содержащими чем преобразования вызова метода или присвоение: бросок может сделать любое разрешенное преобразование кроме преобразования строк.

Преобразование набора значений (§5.1.8) применяется после преобразования типов.

Некоторые броски могут быть доказаны неправильными во время компиляции; такие броски приводят к ошибке времени компиляции.

Значение типа примитива может быть брошено к другому типу примитива преобразованием идентификационных данных, если типы являются тем же самым, или расширяющимся примитивным преобразованием или сужающимся примитивным преобразованием.

Значение типа примитива не может быть брошено к ссылочному типу, бросая преобразование, ни может значение ссылочного типа быть брошенным к типу примитива.

Остающиеся случаи включают преобразование между ссылочными типами. Подробные правила для проверки правильности времени компиляции преобразования кастинга значения ссылочного типа времени компиляции S (источник) к ссылочному типу времени компиляции T (цель) следующие:

См. §8 для спецификации классов, §9 для интерфейсов, и §10 для массивов.

Если бросок к ссылочному типу не является ошибкой времени компиляции, есть два случая:

Если исключение на этапе выполнения бросается, это - a 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 Числовые Продвижения

Числовое продвижение применяется к операндам арифметического оператора. Числовые контексты продвижения позволяют использование преобразования идентификационных данных (§5.1.1) или расширяющегося примитивного преобразования (§5.1.2).

Числовые продвижения используются, чтобы преобразовать операнды числового оператора к общему типу так, чтобы работа могла быть выполнена. Два вида числового продвижения являются унарным числовым продвижением (§5.6.1) и двоичным числовым продвижением (§5.6.2). Аналогичные преобразования в C вызывают "обычными унарными преобразованиями" и "обычными двоичными преобразованиями."

Числовое продвижение не является общей функцией языка программирования Java, а скорее свойством определенных определений встроенных операций.

5.6.1 Унарное Числовое Продвижение

Некоторые операторы применяют унарное числовое продвижение единственному операнду, который должен произвести значение числового типа:

В любом случае тогда применяется преобразование набора значений (§5.1.8).

Унарное числовое продвижение выполняется по выражениям в следующих ситуациях:

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

5.6.2 Двоичное Числовое Продвижение

Когда оператор применяет двоичное числовое продвижение паре операндов, каждый из которых должен обозначить значение числового типа, следующие правила применяются, в порядке, используя расширяющееся преобразование (§5.1.2), чтобы преобразовать операнды по мере необходимости:

После преобразования типов, если таковые вообще имеются, преобразование набора значений (§5.1.8) применяется к каждому операнду.

Двоичное числовое продвижение выполняется на операндах определенных операторов:

Пример двоичного числового продвижения появляется выше в §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);
	}
}
который производит вывод:

7
0.25
Пример преобразовывает символ ASCII G к г управления ASCII (BEL), маскируя от всех кроме низких 5 битов символа. 7 числовое значение этого управляющего символа.


Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй Выпуск
Авторское право © Sun Microsystems, Inc 2000 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к jls@java.sun.com