2.3 Conversions
In this section we discuss the different kinds of type conversions that can be applied to values; in the next section we discuss the contexts in which these conversions are permitted. Some type conversions must be explicitly stated in the program, while others are performed implicitly. Some type conversions can be checked at compile time to guarantee their validity at runtime, while others will require an extra check at runtime.
Widening and Narrowing Primitive Conversions
For the primitive data types, the value of a narrower data type can be converted to a value of a wider data type. This is called a widening primitive conversion. Widening conversions from one primitive type to the next wider primitive type are summarized in Figure 2.2. The conversions shown are transitive. For example, an int can be directly converted to a double without first having to convert it to a long and a float.
Figure 2.2 Widening Primitive Conversions
Note that the target type of a widening primitive conversion has a wider range of values than the source type—for example, the range of the long type subsumes the range of the int type. In widening conversions between integral types, the source value remains intact, with no loss of magnitude information. However, a widening conversion from an int or a long value to a float value, or from a long value to a double value, may result in a loss of precision. The floating-point value in the target type is then a correctly rounded approximation of the integer value. Note that precision relates to the number of significant bits in the value, and must not be confused with magnitude, which relates to how large the represented value can be.
Converting from a wider primitive type to a narrower primitive type is called a narrowing primitive conversion; it can result in a loss of magnitude information, and possibly in a loss of precision as well. Any conversion that is not a widening primitive conversion according to Figure 2.2 is a narrowing primitive conversion. The target type of a narrowing primitive conversion has a narrower range of values than the source type—for example, the range of the int type does not include all the values in the range of the long type.
Note that all conversions between char and the two integer types byte and short are considered narrowing primitive conversions. The reason is that the conversions between the unsigned type char and the signed types byte and short can result in a loss of information. These narrowing conversions are done in two steps: first converting the source value to the int type, and then converting the int value to the target type.
Widening primitive conversions are usually done implicitly, whereas narrowing primitive conversions usually require a cast (p. 48). It is not illegal to use a cast for a widening conversion. However, the compiler will flag any conversion that requires a cast if none has been specified. Regardless of any loss of magnitude or precision, widening and narrowing primitive conversions never result in a runtime exception.
long year = 2020; // (1) Implicit widening: long <—– int, assigned 2020L
int pi = (int) 3.14; // (2) Narrowing requires cast: int <—– double, assigned 3
Ample examples of widening and narrowing primitive conversions can be found in this chapter.