3.3 Conversions

In this section we discuss the different kinds of type conversions and list the contexts in which these can occur. Some type conversions must be explicitly stated in the program, while others are done 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.

Unary Cast Operator: (type)

Java, being a strongly typed language, checks for type compatibility (i.e., checks if a type can substitute for another type in a given context) at compile time. However, some checks are only possible at runtime (for example, which type of object a reference actually denotes during execution). In cases where an operator would have incompatible operands (for example, assigning a double to an int), Java demands that a cast be used to explicitly indicate the type conversion. The cast construct has the following syntax:


(<type>) <expression>

The cast (<type>) is applied to the value of the <expression>. At runtime, a cast results in a new value of <type>, which best represents the value of the <expression> in the old type. We use the term casting to mean applying the cast operator for explicit type conversion.

Casting can be applied to primitive values as well as references. Casting between primitive data types and reference types is not permitted. Boolean values cannot be cast to other data values, and vice versa. The reference literal null can be cast to any reference type.

Examples of casting between primitive data types are provided in this chapter. Casting between references is discussed in Section 6.6 on page 264.

Narrowing and Widening Conversions

For the primitive data types, the value of a narrower data type can be converted to a value of a broader data type without loss of information. This is called a widening primitive conversion. Widening conversions to the next broader type for primitive data types are summarized in Figure 3.1. 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 3.1. Widening Numeric Conversions

graphics/03fig01.gif

Converting from a broader data type to a narrower data type is called a narrowing primitive conversion, which can result in loss of magnitude information. Any conversion which is not a widening conversion according to Figure 3.1 is a narrowing conversion. Note that all conversions between char and the two integer types byte and short are considered narrowing conversions: the reason being that the conversions between the unsigned type char and the signed types byte or short can result in loss of information.

Widening and narrowing conversions are also defined for reference types and are discussed in Section 6.6. Conversions up the inheritance hierarchy are called widening reference conversions (a.k.a. upcasting). Conversions down the inheritance hierarchy are called narrowing reference conversions (a.k.a. downcasting).

Both narrowing and widening conversions can be either explicit (requiring a cast) or implicit. Widening conversions are usually done implicitly, whereas narrowing conversions typically require a cast. It is not illegal to use a cast for a widening conversion. However, the compiler will flag any conversions that require a cast.

Numeric Promotions

Numeric operators only allow operands of certain types. Numeric promotion is implicitly applied on the operands to convert them to permissible types. Distinction is made between unary and binary numeric promotion.

Unary Numeric Promotion

Unary numeric promotion states that

If the single operand of the operator has a type narrower than int, it is converted to int by an implicit widening primitive conversion; otherwise, it is not converted.

In other words, unary numeric promotion converts operands of byte, short, and char to int by applying an implicit widening conversion, but operands of other numeric types are not affected.

Unary numeric promotion is applied in the following contexts:

  • operand of the unary arithmetic operators + and - (see Section 3.5, p. 51)

  • operand of the unary integer bitwise complement operator ~ (see Section 3.13, p. 75)

  • during array creation; for example, new int[20], where the dimension expression (in this case 20) must evaluate to an int value (see Section 4.1, p. 101)

  • indexing array elements; for example, table['a'], where the index expression (in this case 'a') must evaluate to an int value (see Section 4.1, p. 103)

  • individual operands of the shift operators <<, >> and >>> (see Section 3.14, p. 79)

Binary Numeric Promotion

Binary numeric promotion implicitly applies appropriate widening primitive conversions so that a pair of operands have the broadest numeric type of the two, which is always at least int. Given T to be the broadest numeric type of the two operands, the operands are promoted as follows under binary numeric promotion.

If T is broader than int, both operands are converted to T; otherwise, both operands are converted to int.

This means that byte, short, and char are always converted to int at least.

Binary numeric promotion is applied in the following contexts:

  • operands of the arithmetic operators *, /, %, +, and - (see Section 3.5, p. 51)

  • operands of the relational operators <, <=, >, and >= (see Section 3.9, p. 67)

  • operands of the numerical equality operators == and != (see Section 3.10, p. 67)

  • operands of the integer bitwise operators &, ^, and | (see Section 3.13, p. 75)

Type Conversion Contexts

Type conversions can occur in the following contexts:

  • assignments involving primitive data types (see Section 3.4, p. 48) and reference types (see Section 6.6, p. 260)

  • method invocation involving parameters of primitive data types (see Section 3.17, p. 86) and reference types (see Section 6.6, p. 260)

  • arithmetic expression evaluation involving numeric types (see Section 3.5, p. 57)

  • string concatenation involving objects of class String and other data types (see Section 3.6, p. 62)