What are all of the different kinds of type casting in Java and what do they mean?
Created May 4, 2012
Type casting conversions are covered in Section 5.5 of the JLS. See the JLS for all of the gory details.
There are five (5) categories of legal type casting conversions: identity conversion, narrowing and widening primitive conversions, narrowing and widening reference conversions. There's also a category of "forbidden" conversions.
Note that value set conversion is a necessary adjunct to type casting conversions. Value set conversion happens after the type casting conversion dealing with floating point types and is covered in Section 5.1.8 of the JLS.
Note also that there are implicit conversions which are applied in to the expressions in assignments (aka "assignment conversion") and to the actual arguments in method invocations (aka "method invocation conversion"). Assignment conversion is covered in Sections 5.2 and 15.26 of the JLS and method invocation conversion is covered in Sections 5.3, 15.9, and 15.12 of the JLS.
The forbidden conversions... In general, primitive types cannot
have their values casted to a reference type and, conversely, reference
types cannot have their values casted to a primitive type. The
null
type cannot be converted to/from any primitive type.
Array types may not be converted to each other unless their base types are
convertible to each other. Arrays can't be converted to a class type other
than Object
or String
. The boolean
type can only be converted to boolean
and to
String
and nothing else can be converted to
boolean
except itself. For the exhaustive list, see section
5.1.7 of the JLS.
In addition, String
casting is simple... You can't do
it. Yes, it's true that all types, including null
, can be
converted to String
but you may not cast
anything to the String
type (with the single exception of the
identity conversion). This is covered in Sections 5.1.6 and 5.4 of the
JLS.
Identity conversion is also simple. You can convert anything to the
same type as itself. I.e., int
to int
. While
this may seem silly, it's an important base case which allows for things
like redundant casts.
Primitive types can have their values cast to other primitive types via identity conversion or narrowing or widening primitive conversions. A key point to note is that, by definition, neither narrowing nor widening primitive conversions will generate run-time exceptions.
Narrowing primitive conversion is the conversion of a larger type to a
smaller type. I.e., int
to byte
. Given that the
smaller type cannot represent all of the possible values of the larger
type, narrowing primitive conversions may lose information. For example,
narrowing of integral types is performed by discarding all but the number
of lowest order bits which will fit into the target type. See Section
5.1.3 of the JLS for rest of the conversion details.
Widening primitive conversion is the conversion of a smaller primitive
type to a larger primtive type. I.e., short
to
long
. Widening of integral types to other integral types and
from float
to double
are precise and lose no
numeric value information. Widening conversions from integral types to
floating point types may result in a loss of precision due to the nature of
floating point representations. See Section 5.1.2 of the JLS for the rest
of the details.
Reference types can have their values cast to other reference types
via identity conversion or narrowing or widening conversions and a special
conversion of the null
type to any reference type.
A quick aside on alternative nomenclature... A good number of people use the terms "upcasting" and "downcasting" when talking about type casting references. Their notion of "up" and "down" relate to their view of class hierarchies being drawn with the root class at the top and the subclasses drawn successively lower. [They also call them inheritance trees even though they've drawn the tree upside down. :-)] In that model, "upcasting" is equivalent to a "widening reference conversion" and "downcasting" is equivalent to a "narrowing reference conversion." Due to the potential confusion, I recommend that you refrain from using the "upcasting" and "downcasting" terminology.
A narrowing reference conversion is the conversion of a reference to a
given type to a reference of another type which is more restrictive and
type compatible. I.e., Number
to Integer
(since
Integer is a sub-class of Number). Similarly, you can cast a
super-interface to any of it's sub-interfaces. You can cast
Object
references to any class, interface, or array type. See
Section 5.1.5 of the JLS for comprehensive coverage.
Note very clearly that narrowing reference conversions require a
run-time test to make sure that the actual reference object is allowed to
be referenced by the narrowed typed reference. If it's not then the
run-time system will throw a ClassCastException
. You may, of
course, use the instanceof
operator to check that yourself.
A widening reference conversion is the conversion of a reference to a
given type to a reference of another type which is less restrictive and
type compatible. I.e., Integer
to Number
(since
Integer is a sub-class of Number). Similarly, you may cast a sub-interface
to any of its super-interfaces. Also, you may cast a class to any of the
interfaces which it implements. Any class or interface reference to the
Object
type. Casts are also allowed from null
to
any class, interface, or array type. For the full list, read Section 5.1.4
of the JLS.
Note that widening reference conversions require only a source compile-time check for correctness and therefore they do not throw any sort of run-time exception.