■ Some
casting situations:
int x = 4096L; This is illegal. It requires an int downward cast like int x = (int) 4096L;
short
sh = (short) 32800; This is over the 32,767 limit for short. It compiles but you get garbage with a
negative leading bit.
byte
b = 0;
b
+= 9; This works, as b receives implicit int casting. See widening
rules
byte
b = 0;
b = b + 9; The b = b + 9 is loss of precision as the 9 is an int, so it needs an explicit cast with complete enclosing parens. i.e. b = ( byte ) ( b+9 );
■ Narrowing primitive conversions simply lop
off the appropriate number of lead bits. The
sign can change. i.e.
int
x = 129;
byte
b = ( byte ) x; Here b
becomes -127, not 129 as expected,
because the bit in x which corresponds to b's sign
happened to be on.
■ Upward conversions (like byte to char) always require explicit casts. i.e.
byte
b = 0;
char c = '0';
Here both b = c; and c = b; would require casts
■ You cannot convert a char (16 bit unsigned) to a short (also 16 bit) without explicit (short) casting. i.e.
short
sh = 0;
char c = '0';
Here you need sh = ( short ) c; and c = ( char ) sh; to make the assignments.
■ When going from char (16 bit unsigned) to short (16 bit signed), the short takes on the char’s lead bit as its
sign.
i.e.
char
c = 0x8001;
short
sh = ( short ) c; sh is then negative, -32767, because its lead bit
was on.
■ The
default for floating point is double. i.e.
float
f = 2.0;
requires
explicit casting to float
or f.
float
f = 2.0f; works.
float
f = ( float ) 2.0; works.
■ Objects
retain their real identity (from the right hand side) after creation
casting. i.e.
Object
Obj = new Float( 1 );
Float
F = new Float( 2 );
if
( Obj.equals( F ) )
uses the equals(...) method from the Float class, not Object's version.
■ Method
references are resolved at run time, using the object's actual
type (again taken from the right hand side). Variable references
are resolved at compile time, using the object's declared type from the
left.
The following example illustrates how you can change
which runtime go( ) method is selected by
casting object B. But such (run time)
casting cannot change which (compile time) variable s is selected for
printing. Boat's version of s
is always used. The program prints:
Sub's
go( )
Sub's
go( )
Boat's
s
public
class RunTime {
public
static void main(String[ ] args) {
Boat
B = new Sub( ); // B gets its
runtime type here from the right, its declared type from the left
Sub
S = new Sub( );
B.go( ); //
here B's runtime type via casting is used to find the method
S.go( );
B =
S; //
this statement has no effect on method or variable selection
System.out.println( B.s ); //
here B's compile time declared type (Boat) is used to find the variable
}
}
. . . .
class Boat {
String s
= "Boat's s";
public
void go( ) {
System.out.println( "Boat's go( )" );
}
}
class Sub extends Boat {
String s
= "Sub's s";
public
void go( ) {
System.out.println(
"Sub's go( )" );
}
}
■ Object
assignment casting tells the compiler to ignore it until
run time. But even then the cast still can’t go up the object
hierarchy. No narrowing object casting
is allowed to presumably "smaller" objects up the hierarchy. i.e.
Baseclass
B = new Baseclass( );
Subclass
S = ( Subclass ) B; will compile but will give a runtime error
because you don’t know what’s extra in Subclass, which is presumably
larger than Baseclass. So B can’t be presumed to provide
it.
Saying S = B; with no cast to Subclass, will not even
compile.
■ null objects are not immune
from the no downward casting rule. i.e.
Object
o = null;
String
s = null;
s
= o; will not compile, as you
can’t assign upwards without a cast.
s
= ( String ) o; works here.
■ You cannot cast null to anything.