CHAPTER 5
Every Java expression has a type that can be deduced from the structure of the
expression and the types of the literals, variables, and methods mentioned in the
expression. It is possible, however, to write an expression in a context where the
type of the expression is not appropriate. In some cases, this leads to an error at
compile time; for example, if the expression in an if
statement (§14.8) has any
type other than boolean
, a compile-time error occurs. In other cases, the context
may be able to accept a type that is related to the type of the expression; as a convenience,
rather than requiring the programmer to indicate a type conversion
explicitly, the Java language performs an implicit conversion from the type of the
expression to a type acceptable for its surrounding context.
A specific conversion from type S to type T allows an expression of type S to be treated at compile time as if it had type T instead. In some cases this will require a corresponding action at run time to check the validity of the conversion or to translate the run-time value of the expression into a form appropriate for the new type T. For example:
Object
(§20.1) to type Thread
(§20.20) requires a run-time check to make sure that the run-time value is actually an instance of class Thread
or one of its subclasses; if it is not, an exception is thrown.
Thread
to type Object
requires no run-time action; Thread
is a subclass of Object
, so any reference produced by an expression of type Thread
is a valid reference value of type Object
.
int
to type long
requires run-time sign-extension of a 32-bit integer value to the 64-bit long
representation. No information is lost.
double
to type long
requires a nontrivial translation from a 64-bit floating-point value to the 64-bit integer representation. Depending on the actual run-time value, information may be lost.
One conversion context is the operand of a numeric operator such as +
or *
. The conversion process for such operands is called numeric promotion. Promotion is special in that, in the case of binary operators, the conversion chosen for one operand may depend in part on the type of the other operand expression.
This chapter first describes the six categories of conversions (§5.1), including the special conversions to String
allowed for the string concatenation operator +
. Then the five conversion contexts are described:
String
.
class Test { public static void main(String[] args) { // Casting conversion (§5.4) of a float literal to // type int. Without the cast operator, this would // be a compile-time error, because this is a // narrowing conversion (§5.1.3): int i = (int)12.5f;which produces the output:
// String conversion (§5.4) of i's int value: System.out.println("(int)12.5f==" + i);
// Assignment conversion (§5.2) of i's value to type // float. This is a widening conversion (§5.1.2): float f = i;
// String conversion of f's float value: System.out.println("after float widening: " + f);
// Numeric promotion (§5.6) of i's value to type // float. This is a binary numeric promotion. // After promotion, the operation is float*float: System.out.print(f); f = f * i;
// Two string conversions of i and f: System.out.println("*" + i + "==" + f);
// Method invocation conversion (§5.3) of f's value // to type double, needed because the method Math.sin // accepts only a double argument: double d = Math.sin(f);
// Two string conversions of f and d: System.out.println("Math.sin(" + f + ")==" + d);
}
}
(int)12.5f==12 after float widening: 12.0 12.0*12==144.0 Math.sin(144.0)==-0.49102159389846934
The only permitted conversion that involves the type boolean
is the identity conversion from boolean
to boolean
.
byte
to short
, int
, long
, float
, or double
short
to int
, long
, float
, or double
char
to int
, long
, float
, or double
int
to long
, float
, or double
long
to float
or double
float
to double
float
to double
do not lose any information at all; the numeric value is preserved exactly. Conversion of an int
or a long
value to float
, or of a long
value to double
, may result in loss of precision-that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode (§4.2.4).A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format. A widening conversion of a character to an integral type T zero-extends the representation of the character value to fill the wider format.
Despite the fact that loss of precision may occur, widening conversions among primitive types never result in a run-time exception (§11).
Here is an example of a widening conversion that loses precision:
class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }which prints:
-46thus indicating that information was lost during the conversion from type
int
to
type float
because values of type float
are not precise to nine significant digits.
byte
to char
short
to byte
or char
char
to byte
or short
int
to byte
, short
, or char
long
to byte
, short
, char
, or int
float
to byte
, short
, char
, int
, or long
double
to byte
, short
, char
, int
, long
, or float
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.
A narrowing conversion of a character to an integral type T likewise simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the resulting value to be a negative number, even though characters represent 16-bit unsigned integer values.
A narrowing conversion of a floating-point number to an integral type T takes two steps:
long
, if T is long
, or to an int
, if T is byte
, short
, char
, or int
, as follows:
int
or long
0
.
long
, and this integer value can be represented as a long
, then the result of the first step is the long
value V.
int
, then the result of the first step is the int
value V.
int
or long
.
int
or long
.
int
or long
, the result of the conversion is the result of the first step.
byte
, char
, or short
, the result of the conversion is the result of a narrowing conversion to type T (§5.1.3) of the result of the first step.
class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } }produces the output:
long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1The results for
char
, int
, and long
are unsurprising, producing the minimum and maximum representable values of the type.
The results for byte
and short
lose information about the sign and magnitude of the numeric values and also lose precision. The results can be understood by examining the low order bits of the minimum and maximum int.
The minimum int
is, in hexadecimal, 0x80000000
, and the maximum int
is 0x7fffffff
. This explains the short
results, which are the low 16 bits of these values, namely, 0x0000
and 0xffff
; it explains the char
results, which also are the low 16 bits of these values, namely, '\u0000'
and '\uffff'
; and it explains the byte
results, which are the low 8 bits of these values, namely, 0x00
and 0xff
.
A narrowing conversion from double
to float
behaves in accordance with IEEE 754. The result is correctly rounded using IEEE 754 round-to-nearest mode. A value too small to be represented as a float
is converted to positive or negative zero; a value too large to be represented as a float
is converted to a (positive or negative) infinity. A double
NaN is always converted to a float
NaN.
Despite the fact that overflow, underflow, or other loss of information may occur, narrowing conversions among primitive types never result in a run-time exception (§11).
Here is a small test program that demonstrates a number of narrowing conversions that lose information:
class Test { public static void main(String[] args) { // A narrowing of int to short loses high bits: System.out.println("(short)0x12345678==0x" + Integer.toHexString((short)0x12345678));This test program produces the following output:
// A int value not fitting in byte changes sign and magnitude: System.out.println("(byte)255==" + (byte)255);
// A float value too big to fit gives largest int value: System.out.println("(int)1e20f==" + (int)1e20f);
// A NaN converted to int yields zero: System.out.println("(int)NaN==" + (int)Float.NaN);
// A double value too large for float yields infinity: System.out.println("(float)-1e100==" + (float)-1e100);
// A double value too small for float underflows to zero: System.out.println("(float)1e-50==" + (float)1e-50);
}
}
(short)0x12345678==0x5678 (byte)255==-1 (int)1e20f==2147483647 (int)NaN==0 (float)-1e100==-Infinity (float)1e-50==0.0
Object
from any other class type.)
Object
.
Object
.
Cloneable
.
[]
to any array type TC[]
, provided that SC and TC are reference types and there is a widening conversion from SC to TC.
See §8 for the detailed specifications for classes, §9 for interfaces, and §10 for arrays.
Object
to any other class type.)
Object
to any interface type.)
Object
to any array type.
Object
to any interface type.
final
.
final
, provided that T implements J.
[]
to any array type TC[]
, provided that SC and TC are reference types and there is a narrowing conversion from SC to TC.
ClassCastException
is
thrown.
String
from every other type, including the
null type.
boolean
other than the identity conversion.
boolean
other than the identity conversion and string conversion.
final
and does not implement K.
Object
.
final
and does not implement J.
Object
or String
.
Cloneable
, which is implemented by all arrays.
[]
to array type TC[]
if there is no permitted conversion other than a string conversion from SC to TC.
int
.
byte
, short
, or char
.
If the type of an expression can be converted to the type a variable by assignment conversion, we say the expression (or its value) is assignable to the variable or, equivalently, that the type of the expression is assignment compatible with the type of the variable.
An assignment conversion never causes an exception. (Note, however, that an assignment may result in an exception in a special case involving array elements -see §10.10 and §15.25.1.)
The compile-time narrowing of constants means that code such as:
byte theAnswer = 42;is allowed. Without the narrowing, the fact that the integer literal
42
has type int
would mean that a cast to byte
would be required:
byte theAnswer = (byte)42; // cast is permitted but not requiredA value of primitive type must not be assigned to a variable of reference type; an attempt to do so will result in a compile-time error. A value of type
boolean
can be assigned only to a variable of type boolean
.The following test program contains examples of assignment conversion of primitive values:
class Test { public static void main(String[] args) { short s = 12; // narrow 12 to short float f = s; // widen short to float System.out.println("f=" + f); char c = '\u0123'; long l = c; // widen char to long System.out.println("l=0x" + Long.toString(l,16)); f = 1.23f; double d = f; // widen float to double System.out.println("d=" + d); } }It produces the following output:
f=12.0 i=0x123 d=1.2300000190734863The following test, however, produces compile-time errors:
class Test { public static void main(String[] args) { short s = 123; char c = s; // error: would require cast s = c; // error: would require cast } }because not all
short
values are char
values, and neither are all char
values
short
values.
A value of reference type must not be assigned to a variable of primitive type; an attempt to do so will result in a compile-time error.
A value of the null type (the null reference is the only such value) may be assigned to any reference type, resulting in a null reference of that type.
Here is a sample program illustrating assignments of references:
public class Point { int x, y; }Assignment of a value of compile-time reference type S (source) to a variable of compile-time reference type T (target) is checked as follows:
public class Point3D extends Point { int z; }
public interface Colorable { void setColor(int color); }
public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } }
class Test { public static void main(String[] args) { // Assignments to variables of class type: Point p = new Point(); p = new Point3D(); // ok: because Point3d is a // subclass of Point Point3D p3d = p; // error: will require a cast because a // Point might not be a Point3D // (even though it is, dynamically, // in this example.) // Assignments to variables of type Object: Object o = p; // ok: any object to Object int[] a = new int[3]; Object o2 = a; // ok: an array to Object
// Assignments to variables of interface type: ColoredPoint cp = new ColoredPoint(); Colorable c = cp; // ok: ColoredPoint implements // Colorable
// Assignments to variables of array type: byte[] b = new byte[4]; a = b; // error: these are not arrays // of the same primitive type Point3D[] p3da = new Point3D[3]; Point[] pa = p3da; // ok: since we can assign a // Point3D to a Point p3da = pa; // error: (cast needed) since a Point // can't be assigned to a Point3D
}
}
Object
, or a compile-time error occurs.
[]
, that is, an array of components of type SC:
Object
, or a compile-time error occurs.
Cloneable
, the only interface implemented by arrays.
[]
, that is, an array of components of type TC, then a compile-time error occurs unless one of the following is true:
The following test program illustrates assignment conversions on reference values, but fails to compile because it violates the preceding rules, as described in its comments. This example should be compared to the preceding one.
public class Point { int x, y; }Here is another example involving assignment of array objects:
public interface Colorable { void setColor(int color); }
public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } }
class Test { public static void main(String[] args) {
Point p = new Point();
ColoredPoint cp = new ColoredPoint(); // Okay because ColoredPoint is a subclass of Point: p = cp;
// Okay because ColoredPoint implements Colorable: Colorable c = cp;
// The following cause compile-time errors because // we cannot be sure they will succeed, depending on // the run-time type of p; a run-time check will be // necessary for the needed narrowing conversion and // must be indicated by including a cast: cp = p; // p might be neither a ColoredPoint // nor a subclass of ColoredPoint c = p; // p might not implement Colorable
}
}
class Point { int x, y; }In this example:
class ColoredPoint extends Point { int color; }
class Test { public static void main(String[] args) { long[] veclong = new long[100]; Object o = veclong; // okay Long l = veclong; // compile-time error short[] vecshort = veclong; // compile-time error Point[] pvec = new Point[100]; ColoredPoint[] cpvec = new ColoredPoint[100]; pvec = cpvec; // okay pvec[0] = new Point(); // okay at compile time, // but would throw an // exception at run time cpvec = pvec; // compile-time error } }
veclong
cannot be assigned to a Long
variable, because Long
is a class type (§20.8) other than Object
. An array can be assigned only to a variable of a compatible array type, or to a variable of type Object
.
veclong
cannot be assigned to vecshort
, because they are arrays of primitive type, and short
and long
are not the same primitive type.
cpvec
can be assigned to pvec
,
because any reference that could be the value of an expression of type ColoredPoint
can be the value of a variable of type Point
. The subsequent assignment of the new Point
to a component of pvec
then would throw an ArrayStoreException
(if the program were otherwise corrected so that it could be compiled), because a ColoredPoint
array can't have an instance of Point
as the value of a component.
pvec
cannot be assigned to cpvec
,
because not every reference that could be the value of an expression of type ColoredPoint
can correctly be the value of a variable of type Point
. If the value of pvec
at run time were a reference to an instance of Point[]
, and the assignment to cpvec
were allowed, a simple reference to a component of cpvec
, say, cpvec[0]
, could return a Point
, and a Point
is not a ColoredPoint
. Thus to allow such an assignment would allow a violation of the type system. A cast may be used (§5.4, §15.15) to ensure that pvec
references a ColoredPoint[]
:
cpvec = (ColoredPoint[])pvec; // okay, but may throw an // exception at run time
Method invocation conversions specifically do not include the implicit narrowing of integer constants which is part of assignment conversion (§5.2). The Java designers felt that including these implicit narrowing conversions would add additional complexity to the overloaded method matching resolution process (§15.11.2). Thus, the example:
class Test {causes a compile-time error because the integer literals
static int m(byte a, int b) { return a+b; }
static int m(short a, short b) { return a-b; }
public static void main(String[] args) { System.out.println(m(12, 2)); // compile-time error }
}
12
and 2
have type int
, so
neither method m
matches under the rules of (§15.11.2). A language that included
implicit narrowing of integer constants would need additional rules to resolve
cases like this example.
+
operator when one
of the arguments is a String
. In this single special case, the other argument to the
+
is converted to a String
, and a new String
which is the concatenation of the
two strings is the result of the +
. String conversion is specified in detail within the
description of the string concatenation +
operator (§15.17.1).
Some casts can be proven incorrect at compile time; such casts result in a compile-time error.
A value of a primitive type can be cast to another primitive type by identity conversion, if the types are the same, or by a widening primitive conversion or a narrowing primitive conversion.
A value of a primitive type cannot be cast to a reference type by casting conversion, nor can a value of a reference type be cast to a primitive type.
The remaining cases involve conversion between reference types. The detailed rules for compile-time correctness checking of a casting conversion of a value of compile-time reference type S (source) to a compile-time reference type T (target) are as follows:
final
class (§8.1.2), then the cast is always correct at compile time (because even if S does not implement T, a subclass of S might).
final
class (§8.1.2), then S must implement T, or a compile-time error occurs.
Object
, or a compile-time error occurs.
final
(§8.1.2), then the cast is always correct at compile time (because even if T does not implement S, a subclass of T might).
final
(§8.1.2), then T must implement S, or a compile-time error occurs.
[]
, that is, an array of components of type SC:
Object
, then a compile-time error occurs (because Object
is the only class type to which arrays can be assigned).
Cloneable
, the only interface implemented by arrays.
[]
, that is, an array of components of type TC, then a compile-time error occurs unless one of the following is true:
If a cast to a reference type is not a compile-time error, there are two cases:
null
, then the cast is allowed. Otherwise, let R be the class of the object referred to by the run-time reference value, and let T be the type named in the cast operator. A cast conversion must check, at run time, that the class R is assignment compatible with the type T, using the algorithm specified in §5.2 but using the class R instead of the compile-time type S as specified there. (Note that R cannot be an interface when these rules are first applied for any given cast, but R may be an interface if the rules are applied recursively because the run-time reference value refers to an array whose element type is an interface type.) This modified algorithm is shown here:
Object
(§4.3.2, §20.1), or a run-time exception is thrown.
[]
-that is, an array of components of type RC:
Object
(§4.3.2, §20.1), or a run-time exception is thrown.
Cloneable
, the only interface implemented by arrays (this case could slip past the compile-time checking if, for example, a reference to an array were stored in a variable of type Object
).
[]
, that is, an array of components of type TC, then a run-time exception is thrown unless one of the following is true:
ClassCastException
(§11.5.1.1,
§20.22).
Here are some examples of casting conversions of reference types, similar to the example in §5.2:
public class Point { int x, y; }Here the first compile-time error occurs because the class types
public interface Colorable { void setColor(int color); }
public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } }
final class EndPoint extends Point { }
class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); Colorable c;
// The following may cause errors at run time because // we cannot be sure they will succeed; this possibility // is suggested by the casts: cp = (ColoredPoint)p; // p might not reference an // object which is a ColoredPoint // or a subclass of ColoredPoint c = (Colorable)p; // p might not be Colorable
// The following are incorrect at compile time because // they can never succeed as explained in the text: Long l = (Long)p; // compile-time error #1 EndPoint e = new EndPoint(); c = (Colorable)e; // compile-time error #2
}
}
Long
and Point
are unrelated (that is, they are not the same, and neither is a subclass of the other),
so a cast between them will always fail.
The second compile-time error occurs because a variable of type EndPoint
can never reference a value that implements the interface Colorable
. This is because EndPoint
is a final
type, and a variable of a final
type always holds a value of the same run-time type as its compile-time type. Therefore, the run-time type of variable e
must be exactly the type EndPoint
, and type EndPoint
does not implement Colorable
.
Here is an example involving arrays (§10):
class Point { int x, y;This example compiles without errors and produces the output:
Point(int x, int y) { this.x = x; this.y = y; }
public String toString() { return "("+x+","+y+")"; }
}
public interface Colorable { void setColor(int color); }
public class ColoredPoint extends Point implements Colorable { int color; ColoredPoint(int x, int y, int color) { super(x, y); setColor(color); }
public void setColor(int color) { this.color = color; }
public String toString() { return super.toString() + "@" + color; }
}
class Test { public static void main(String[] args) { Point[] pa = new ColoredPoint[4]; pa[0] = new ColoredPoint(2, 2, 12); pa[1] = new ColoredPoint(4, 5, 24); ColoredPoint[] cpa = (ColoredPoint[])pa; System.out.print("cpa: {"); for (int i = 0; i < cpa.length; i++) System.out.print((i == 0 ? " " : ", ") + cpa[i]); System.out.println(" }"); }
}
cpa: { (2,2)@12, (4,5)@24, null, null }The following example uses casts to compile, but it throws exceptions at run time, because the types are incompatible:
public class Point { int x, y; }
public interface Colorable { void setColor(int color); }
public class ColoredPoint extends Point implements Colorable {
int color;
public void setColor(int color) { this.color = color; }
}
class Test { public static void main(String[] args) {
Point[] pa = new Point[100];
// The following line will throw a ClassCastException: ColoredPoint[] cpa = (ColoredPoint[])pa;
System.out.println(cpa[0]);
int[] shortvec = new int[2];
Object o = shortvec;
// The following line will throw a ClassCastException: Colorable c = (Colorable)o;
c.setColor(0);
}
}
Numeric promotions are used to convert the operands of a numeric operator to a common type so that an operation can be performed. The two kinds of numeric promotion are unary numeric promotion (§5.6.1) and binary numeric promotion (§5.6.2). The analogous conversions in C are called "the usual unary conversions" and "the usual binary conversions."
Numeric promotion is not a general feature of Java, but rather a property of the specific definitions of the built-in operations.
byte
, short
, or char
, unary numeric promotion promotes it to a value of type int
by a widening conversion (§5.1.2).
+
(§15.14.3) and minus -
(§15.14.4)
~
(§15.14.5)
>>
, >>>
, and <<
(§15.18), so that a long
shift distance (right operand) does not promote the value being shifted (left operand) to long
class Test { public static void main(String[] args) { byte b = 2; int a[] = new int[b]; // dimension expression promotion char c = '\u0001'; a[c] = 1; // index expression promotion a[0] = -c; // unary - promotion System.out.println("a: " + a[0] + "," + a[1]); b = -1; int i = ~b; // bitwise complement promotion System.out.println("~0x" + Integer.toHexString(b) + "==0x" + Integer.toHexString(i)); i = b << 4L; // shift promotion (left operand) System.out.println("0x" + Integer.toHexString(b) + "<<4L==0x" + Integer.toHexString(i)); } }This test program produces the output:
a: -1,1 ~0xffffffff==0x0 0xffffffff<<4L==0xfffffff0
double
, the other is converted to double
.
float
, the other is converted to float
.
long
, the other is converted to long
.
int
.
*
, /
and %
(§15.16)
+
and -
(§15.17.2)
<
, <=
, >
, and >=
(§15.19.1)
==
and !=
(§15.20.1)
&
, ^
, and |
(§15.21.1)
? :
(§15.24)
class Test { public static void main(String[] args) { int i = 0; float f = 1.0f; double d = 2.0;which produces the output:
// First i*f promoted to float*float, then // float==double is promoted to double==double: if (i * f == d) System.out.println("oops");
// A char&byte is promoted to int&int: byte b = 0x1f; char c = 'G'; int control = c & b; System.out.println(Integer.toHexString(control));
// A int:float promoted to float:float: f = (b==0) ? f : 4.0f; System.out.println(1.0/f);
}
}
7 0.25The example converts the ASCII character
G
to the ASCII control-G (BEL), by
masking off all but the low 5 bits of the character. The 7
is the numeric value of
this control character.
Contents | Prev | Next | Index
Java Language Specification (HTML generated by Suzette Pelouch on February 24, 1998)
Copyright © 1996 Sun Microsystems, Inc.
All rights reserved
Please send any comments or corrections to [email protected]