Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

NARROWING AND WIDENING
CONVERSIONS

Suppose you're doing some C or C++ programming, and you have a
sequence of statements like this:
long a; short b; a = 123456; b = a; What happens? This is probably a programmer error. Here a value (123456) is assigned to a variable (b) that's probably too small to represent it. (This assumes that a value of data type short is 16 bits). C and C++ do not prohibit this operation; the result is implementation-defined. An experiment with a couple of C++ compilers shows a value of -7616 for b. This usage is illegal in the Java(tm) programming language, and it serves to illustrate the way in which the language treats conversions. Specifically, when you convert a larger primitive type (like long) to a smaller primitive type (like short), it requires a cast to be valid. For example: b = (short)a; Converting a large primitive type to a smaller primitive type is called "narrowing primitive conversion". This type of conversion has the potential for some loss of information. That's because all but the lowest bits of data are discarded in the conversion. In this example, the lowest 16 bits are saved, because a short is guaranteed to have exactly 16 bits. By dropping the higher bits of data, you might lose information about the magnitude and precision of the original value. The magnitude is the range of values that a primitive type can represent. For example, if you convert a long to a byte, you'll probably lose magnitude information, because a long can represent a much wider range of values using 64 bits than a byte can using 8 bits. The idea of precision can be illustrated using float and double. You can represent a value like "pi" with more significant digits if you use a double instead of a float. In addition to a loss of magnitude and precision, the sign of the converted value can be different than the sign of the original value. Widening conversions are analogous to narrowing conversions. A widening conversion never loses information about magnitude, but can lose precision. For example, if you convert a long to a float, you might sacrifice precision. A float has 32 bits instead of 64, and uses some of those bits for an exponent. So a float cannot represent all 64 bits of the long value. A float can represent the magnitude of a long, but not necessarily the precision. No cast is required for a widening conversion such as: long a; float f; a = 1234567890; f = a; Narrowing and widening primitive conversions never result in a run-time exception, even in cases where information is lost. There's a special case known as an "assignment conversion" that handles some conversion cases that would otherwise seem to be illegal, such as: byte x; x = 59; In this example, a variable of type byte is being assigned a value of type int. This implies a narrowing conversion. And it's legal if (a) the expression to be assigned (59) is of constant int type, (b) it's being assigned to a variable (x) of type byte, short, or char, and (c) it will fit into the variable without losing any information. In other words, you can assign small integer constants to byte, char, and short variables without worrying about using a cast. Narrowing and widening conversions also apply to reference types. For example, if you have: class A {} class B extends A {} ... A aref = new A(); B bref = new B(); then usage like: aref = bref; is a widening conversion, and usage such as: bref = (B)aref; is a narrowing conversion. Widening reference conversions never throw a run-time exception, but narrowing reference conversions can throw a ClassCastException for usage like this: class A {} class B extends A {} class C extends A {} ... B bref = new B(); A aref = bref; C cref = (C)aref; The variable "aref" references a B, not a C, and an attempt to force the B into a C results in an exception.

Any comments? email to:
richard@dreamscity.net