Spec-Zone .ru
спецификации, руководства, описания, API
|
Contents | Prev | Next | Index | Java Language Specification Third Edition |
CHAPTER 5
Every expression written in the Java programming language 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. 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 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
to type Thread
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.
A conversion from type 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.
In every conversion context, only certain specific conversions are permitted. For convenience of description, the specific conversions that are possible in the Java programming language are grouped into several broad categories:
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 eleven categories of conversions (§5.1), including the special conversions to String
allowed for the string concatenation operator +
. Then the five conversion contexts are described:
OutOfMemoryError
(as a result of boxing conversion (§5.1.7)), a NullPointerException
(as a result of unboxing conversion (§5.1.8)), or a ClassCastException
(as a result of an unchecked conversion (§5.1.9)) to be thrown at run time.
OutOfMemoryError
(as a result of boxing conversion (§5.1.7)), a NullPointerException
(as a result of unboxing conversion (§5.1.8)), or a ClassCastException
(as a result of an unchecked conversion (§5.1.9)) to be thrown at run time.
String
.
Here are some examples of the various contexts for conversion:
which produces the output: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; // 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
This may seem trivial, but it has two practical consequences. First, it is always permitted for an expression to have the desired type to begin with, thus allowing the simply stated rule that every expression is subject to conversion, if only a trivial identity conversion. Second, it implies that it is permitted for a program to include redundant cast operators for the sake of clarity.
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
in strictfp
expressions also preserve the numeric value exactly; however, such conversions that are not strictfp
may lose information about the overall magnitude of the converted value.
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 char
to an integral type T zero-extends the representation of the char
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:
which prints:class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }
thus indicating that information was lost during the conversion from type-46
int
to type float
because values of type float
are not precise to nine significant digits.
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 char
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 char
s 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.
produces the output: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); } }
long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1
The 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
.
Here is a small test program that demonstrates a number of narrowing conversions that lose information:
This test program produces the following output: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)); // 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
byte
to char
byte
is converted to an int
via widening primitive conversion, and then the resulting int
is converted to a char
by narrowing primitive conversion.Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time.
See §8 for the detailed specifications for classes, §9 for interfaces, and §10 for arrays.
Object
to any other reference type.)
final
.
Cloneable
and java.io.Serializable
to any array type T[].
ClassCastException
is thrown.
boolean
to type Boolean
byte
to type Byte
char
to type Character
short
to type Short
int
to type Integer
long
to type Long
float
to type Float
double
to type Double
boolean
, then boxing conversion converts p into a reference r of class and type Boolean
, such that r.booleanValue()
== p
byte
, then boxing conversion converts p into a reference r of class and type Byte
, such that r.byteValue()
== p
char
, then boxing conversion converts p into a reference r of class and type Character
, such that r.charValue()
== p
short
, then boxing conversion converts p into a reference r of class and type Short
, such that r.shortValue()
== p
int
, then boxing conversion converts p into a reference r of class and type Integer
, such that r.intValue()
== p
long
, then boxing conversion converts p into a reference r of class and type Long
, such that r.longValue()
== p
float
then:
Float
, such that r.floatValue()
evaluates to p
Float
such that r.isNaN()
evaluates to true.
double
, then
Double
, such that r.doubleValue()
evaluates to p
Double
such that r.isNaN()
evaluates to true.
true
, false
, a byte
, a char
in the range \u0000 to \u007f, or an int
or short
number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.Discussion
Ideally, boxing a given primitive value p, would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rules above are a pragmatic compromise. The final clause above requires that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly.
For other values, this formulation disallows any assumptions about the identity of the boxed values on the programmer's part. This would allow (but not require) sharing of some or all of these references.
This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all characters and shorts, as well as integers and longs in the range of -32K - +32K.
OutOfMemoryError
if a new instance of one of the wrapper classes (Boolean
, Byte
, Character
, Short
, Integer
, Long
, Float
, or Double
) needs to be allocated and insufficient storage is available.
Boolean
to type boolean
Byte
to type byte
Character
to type char
Short
to type short
Integer
to type int
Long
to type long
Float
to type float
Double
to type double
Boolean
, then unboxing conversion converts r into r.booleanValue()
Byte
, then unboxing conversion converts r into r.byteValue()
Character
, then unboxing conversion converts r into r.charValue()
Short
, then unboxing conversion converts r into r.shortValue()
Integer
, then unboxing conversion converts r into r.intValue()
Long
, then unboxing conversion converts r into r.longValue()
Float
, unboxing conversion converts r into r.floatValue()
Double
, then unboxing conversion converts r into r.doubleValue()
null
, unboxing conversion throws a NullPointerException
SuppressWarnings
annotation (§9.6.1.5)) unless the parameterized type G is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
Discussion
Unchecked conversion is used to enable a smooth interoperation of legacy code, written before the introduction of generic types, with libraries that have undergone a conversion to use genericity (a process we call generification).
In such circumstances (most notably, clients of the collections framework in java.util
), legacy code uses raw types (e.g., Collection
instead of Collection<String>
). Expressions of raw types are passed as arguments to library methods that use parameterized versions of those same types as the types of their corresponding formal parameters.
Such calls cannot be shown to be statically safe under the type system using generics. Rejecting such calls would invalidate large bodies of existing code, and prevent them from using newer versions of the libraries. This in turn, would discourage library vendors from taking advantage of genericity.
To prevent such an unwelcome turn of events, a raw type may be converted to an arbitrary invocation of the generic type declaration the raw type refers to. While the conversion is unsound, it is tolerated as a concession to practicality. A warning (known as an unchecked warning) is issued in such cases.
?
then Si is a fresh type variable whose upper bound is Ui[A1 := S1, ..., An := Sn] and whose lower bound is the null type.
? extends
Bi, then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1 := S1, ..., An := Sn]) and whose lower bound is the null type, where glb(V1,... ,Vm) is V1 & ... & Vm. It is a compile-time error if for any two classes (not interfaces) Vi and Vj,Vi is not a subclass of Vj or vice versa.
? super
Bi, then Si is a fresh type variable whose upper bound is Ui[A1 := S1, ..., An := Sn] and whose lower bound is Bi.
Capture conversion is not applied recursively.
Discussion
Capture conversion is designed to make wildcards more useful. To understand the motivation, let's begin by looking at the methodjava.util.Collections.reverse()
:public static void reverse(List<?> list);
The method reverses the list provided as a parameter. It works for any type of list, and so the use of the wildcard type List<?>
as the type of the formal parameter is entirely appropriate.
Now consider how one would implement reverse()
.
The implementation needs to copy the list, extract elements from the copy , and insert them into the original. To do this in a type safe manner, we need to give a name,public static void reverse(List<?> list) { rev(list);} private static <T> void rev(List<T> list) { List<T> tmp = new ArrayList<T>(list); for (int i = 0; i < list.size(); i++) { list.set(i, tmp.get(list.size() - i - 1)); } }
T
, to the element type of the incoming list. We do this in the private service method rev()
.
This requires us to pass the incoming argument list, of type List<?>
, as an argument to rev()
. Note that in general, List<?>
is a list of unknown type. It is not a subtype of List<T>
, for any type T
. Allowing such a subtype relation would be unsound. Given the method:
public static <T> void fill(List<T> l, T obj)
List<String> ls = new ArrayList<String>(); List<?> l = ls; Collections.fill(l, new Object()); // not really legal - but assume // it was String s = ls.get(0); // ClassCastException - ls contains Objects, //not Strings.
would undermine the type system.
So, without some special dispensation, we can see that the call from reverse()
to rev()
would be disallowed. If this were the case, the author of reverse()
would be forced to write its signature as:
This is undesirable, as it exposes implementation information to the caller. Worse, the designer of an API might reason that the signature using a wildcard is what the callers of the API require, and only later realize that a type safe implementation was precluded.public static <T> void reverse(List<T> list)
The call from reverse()
to rev()
is in fact harmless, but it cannot be justified on the basis of a general subtyping relation between List<?>
and List<T>
. The call is harmless, because the incoming argument is doubtless a list of some type (albeit an unknown one). If we can capture this unknown type in a type variable X
, we can infer T
to be X
. That is the essence of capture conversion. The specification of course must cope with complications, like non-trivial (and possibly recursively defined) upper or lower bounds, the presence of multiple arguments etc.
Discussion
Mathematically sophisticated readers will want to relate capture conversion to established type theory. Readers unfamiliar with type theory can skip this discussion - or else study a suitable text, such as Types and Programming Languages by Benjamin Pierce, and then revisit this section.Here then is a brief summary of the relationship of capture conversion to established type theoretical notions.
Wildcard types are a restricted form of existential types. Capture conversion corresponds loosely to an opening of a value of existential type. A capture conversion of an expression e
, can be thought of as an open of e
in a scope that comprises the top-level expression that encloses e
.
The classical open
operation on existentials requires that the captured type variable must not escape the opened expression. The open
that corresponds to capture conversion is always on a scope sufficiently large that the captured type variable can never be visible outside that scope.
The advantage of this scheme is that there is no need for a close
operation, as defined in the paper On Variance-Based Subtyping for Parametric Types by Atsushi Igarashi and Mirko Viroli, in the proceedings of the 16th European Conference on Object Oriented Programming (ECOOP 2002).
For a formal account of wildcards, see Wild FJ by Mads Torgersen, Erik Ernst and Christian Plesner Hansen, in the 12th workshop on Foundations of Object Oriented Programming (FOOL 2005).
String
from every other type, including the null type. See (§5.4) for details of the string conversion context.Within an expression that is not FP-strict (§15.4), value set conversion provides choices to an implementation of the Java programming language:
float
and is not an element of the float value set, then the implementation must map the value to the nearest element of the float value set. This conversion may result in overflow or underflow.
double
and is not an element of the double value set, then the implementation must map the value to the nearest element of the double value set. This conversion may result in overflow or underflow.
Whether in FP-strict code or code that is not FP-strict, value set conversion always leaves unchanged any value whose type is neither float
nor double
.
Discussion
An example of such an illegal chain would be:
Integer
, Comparable<Integer>
, Comparable
, Comparable<String>
The first three elements of the chain are related by widening reference conversion, while the last entry is derived from its predecessor by unchecked conversion. However, this dis not a valid assignment conversion, because the chain contains two parameterized types, Comparable<Integer>
and Comparable<String>
, that are not subtypes.
In addition, if the expression is a constant expression (§15.28) of type byte
, short
, char
or int
:
byte
, short
, or char
, and the value of the constant expression is representable in the type of the variable.
If the type of the variable is float
or double
, then value set conversion is applied to the value v that is the results of the type conversion:
v
is of type float
and is an element of the float-extended-exponent value set, then the implementation must map v
to the nearest element of the float value set. This conversion may result in overflow or underflow.
v
is of type double
and is an element of the double-extended-exponent value set, then the implementation must map v
to the nearest element of the double value set. This conversion may result in overflow or underflow.
If, after the type conversions above have been applied, the resulting value is an object which is not an instance of a subclass or subinterface of the erasure of the type of the variable, then a ClassCastException
is thrown.
Discussion
This circumstance can only arise as a result of heap pollution (§4.12.2.1).In practice, implementations need only perfom casts when accessing a field or method of an object of parametized type, when the erased type of the field, or the erased result type of the method differ from their unerased type.
The only exceptions that an assignment conversion may cause are:
OutOfMemoryError
as a result of a boxing conversion.
ClassCastException
in the special circumstances indicated above.
NullPointerException
as a result of an unboxing conversion on a null reference.
The compile-time narrowing of constants means that code such as:
is allowed. Without the narrowing, the fact that the integer literalbyte theAnswer = 42;
42
has type int
would mean that a cast to byte
would be required:
byte theAnswer = (byte)42; // cast is permitted but not required
The following test program contains examples of assignment conversion of primitive values:
It produces the following output: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); } }
The following test, however, produces compile-time errors:f=12.0 l=0x123 d=1.2300000190734863
because not allclass Test { public static void main(String[] args) { short s = 123; char c = s; // error: would require cast s = c; // error: would require cast } }
short
values are char
values, and neither are all char
values short
values.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; } 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 }}
The following test program illustrates assignment conversions on reference values, but fails to compile, as described in its comments. This example should be compared to the preceding one.
Here is another example involving assignment of array objects: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 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 } }
In this example:class Point { int x, y; } 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 other than Object
. An array can be assigned only to a variable of a compatible array type, or to a variable of type Object
, Cloneable
or java.io.Serializable
.
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.5, §15.16) to ensure that pvec
references a ColoredPoint[]
:
cpvec = (ColoredPoint[])pvec; // okay, but may throw an // exception at run time
If, after the conversions listed above have been applied, the resulting type is a raw type (§4.8), an unchecked conversion (§5.1.9) may then be applied. It is a compile time error if the chain of conversions contains two parameterized types that are not not in the subtype relation.
If the type of an argument expression is either float
or double
, then value set conversion (§5.1.13) is applied after the type conversion:
float
is an element of the float-extended-exponent value set, then the implementation must map the value to the nearest element of the float value set. This conversion may result in overflow or underflow.
double
is an element of the double-extended-exponent value set, then the implementation must map the value to the nearest element of the double value set. This conversion may result in overflow or underflow.
If, after the type conversions above have been applied, the resulting value is an object which is not an instance of a subclass or subinterface of the erasure of the corresponding formal parameter type, then a ClassCastException
is thrown.
This circumstance can only arise as a result of heap pollution (§4.12.2.1).
Method invocation conversions specifically do not include the implicit narrowing of integer constants which is part of assignment conversion (§5.2). The designers of the Java programming language felt that including these implicit narrowing conversions would add additional complexity to the overloaded method matching resolution process (§15.12.2).
Thus, the example:
causes a compile-time error because the integer literalsclass Test { 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.12.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.18.1).
Value set conversion (§5.1.13) is applied after the type conversion.
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 can be cast to a reference type by boxing conversion (§5.1.7).
A value of a reference type can be cast to a primitive type by unboxing conversion (§5.1.8).
The remaining cases involve conversion of a compile-time reference type S (source) to a compile-time reference type T (target).
A cast from a type S to a type T is statically known to be correct if and only if S <: T (§4.10).
A cast from a type S to a parameterized type (§4.5) T is unchecked unless at least one of the following conditions hold:
An unchecked cast from S to T is completely unchecked if the cast from |S| to |T| is statically known to be correct. Otherwise it is partially unchecked. An unchecked cast causes an unchecked warning to occur (unless it is suppressed using the SuppressWarnings
annotation (§9.6.1.5)).
A cast is a checked cast if it is not statically known to be correct and it is not unchecked.
The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type S to a compile-time reference type T are as follows:
final
class (§8.1.1), then, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if S does not implement T, a subclass of S might).
final
class (§8.1.1), then S must implement T, or a compile-time error occurs.
Object
, or a compile-time error occurs.
final
(§8.1.1), then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might).
final
, then:
Object
, then a compile-time error occurs (because Object
is the only class type to which arrays can be assigned).
java.io.Serializable
or the type Cloneable
, the only interfaces implemented by arrays.
Object
or the type java.io.Serializable
or the type Cloneable
, or a type variable that S could legally be cast to by recursively applying these rules, then the cast is legal (though unchecked).
If a cast to a reference type is not a compile-time error, there are several 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 erasure of 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. (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 may refer to an array whose element type is an interface type.) The algorithm for performing the check is shown here:
Object
(§4.3.2), or a run-time exception is thrown.
Object
(§4.3.2), or a run-time exception is thrown.
java.io.Serializable
or the type Cloneable
, the only interfaces 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
).
ClassCastException
.Here are some examples of casting conversions of reference types, similar to the example in §5.2:
Here the first compile-time error occurs because the class typespublic 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; } } 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):
This example compiles without errors and produces the output:class Point { int x, y; 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).
Byte
, Short
, Character
, or Integer
it is subjected to unboxing conversion. The result is then promoted to a value of type int
by a widening conversion (§5.1.2) or an identity conversion.
Long
, Float
, or Double
it is subjected to unboxing conversion.
byte
, short
, or char
, unary numeric promotion promotes it to a value of type int
by a widening conversion (§5.1.2).
Unary numeric promotion is performed on expressions in the following situations:
+
(§15.15.3)
-
(§15.15.4)
~
(§15.15.5)
>>
, >>>
, or <<
(§15.19); therefore a long
shift distance (right operand) does not promote the value being shifted (left operand) to long
Here is a test program that includes examples of unary numeric promotion:
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
.
Binary numeric promotion is performed on the operands of certain operators:
*
, /
and %
(§15.17)
+
and -
(§15.18.2)
<
, <=
, >
, and >=
(§15.20.1)
==
and !=
(§15.21.1)
&
, ^
, and |
(§15.22.1)
? :
(§15.25)
An example of binary numeric promotion appears above in §5.1. Here is another:
which produces the output:class Test { public static void main(String[] args) { int i = 0; float f = 1.0f; double d = 2.0; // First int*float is 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)); // Here int:float is promoted to float:float: f = (b==0) ? i : 4.0f; System.out.println(1.0/f); } }
7 0.25
The 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 Third Edition |
Copyright © 1996-2005 Sun Microsystems, Inc.
All rights reserved
Please send any comments or corrections via our