457 lines
15 KiB
Plaintext
457 lines
15 KiB
Plaintext
[[painless-casting]]
|
|
=== Casting
|
|
|
|
A cast converts the value of an original type to the equivalent value of a
|
|
target type. An implicit cast infers the target type and automatically occurs
|
|
during certain <<painless-operators, operations>>. An explicit cast specifies
|
|
the target type and forcefully occurs as its own operation. Use the *cast
|
|
operator* to specify an explicit cast.
|
|
|
|
*Errors*
|
|
|
|
* If during a cast there exists no equivalent value for the target type.
|
|
* If an implicit cast is given, but an explicit cast is required.
|
|
|
|
*Grammar*
|
|
[source,ANTLR4]
|
|
----
|
|
cast: '(' TYPE ')' expression
|
|
----
|
|
|
|
*Examples*
|
|
|
|
* Valid casts.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> int i = (int)5L;
|
|
<2> Map m = new HashMap();
|
|
<3> HashMap hm = (HashMap)m;
|
|
----
|
|
+
|
|
<1> declare `int i`;
|
|
explicit cast `long 5` to `int 5` -> `int 5`;
|
|
assign `int 5` to `i`
|
|
<2> declare `Map m`;
|
|
allocate `HashMap` instance -> `HashMap reference`;
|
|
implicit cast `HashMap reference` to `Map reference` -> `Map reference`;
|
|
assign `Map reference` to `m`
|
|
<3> declare `HashMap hm`;
|
|
access `m` -> `Map reference`;
|
|
explicit cast `Map reference` to `HashMap reference` -> `HashMap reference`;
|
|
assign `HashMap reference` to `hm`
|
|
|
|
[[numeric-type-casting]]
|
|
==== Numeric Type Casting
|
|
|
|
A <<primitive-types, numeric type>> cast converts the value of an original
|
|
numeric type to the equivalent value of a target numeric type. A cast between
|
|
two numeric type values results in data loss when the value of the original
|
|
numeric type is larger than the target numeric type can accommodate. A cast
|
|
between an integer type value and a floating point type value can result in
|
|
precision loss.
|
|
|
|
The allowed casts for values of each numeric type are shown as a row in the
|
|
following table:
|
|
|
|
|====
|
|
| | byte | short | char | int | long | float | double
|
|
| byte | | implicit | implicit | implicit | implicit | implicit | implicit
|
|
| short | explicit | | explicit | implicit | implicit | implicit | implicit
|
|
| char | explicit | explicit | | implicit | implicit | implicit | implicit
|
|
| int | explicit | explicit | explicit | | implicit | implicit | implicit
|
|
| long | explicit | explicit | explicit | explicit | | implicit | implicit
|
|
| float | explicit | explicit | explicit | explicit | explicit | | implicit
|
|
| double | explicit | explicit | explicit | explicit | explicit | explicit |
|
|
|====
|
|
|
|
*Examples*
|
|
|
|
* Valid numeric type casts.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> int a = 1;
|
|
<2> long b = a;
|
|
<3> short c = (short)b;
|
|
<4> double e = (double)a;
|
|
----
|
|
+
|
|
<1> declare `int a`;
|
|
assign `int 1` to `a`
|
|
<2> declare `long b`;
|
|
access `a` -> `int 1`;
|
|
implicit cast `int 1` to `long 1` -> `long 1`;
|
|
assign `long 1` to `b`
|
|
<3> declare `short c`;
|
|
access `b` -> `long 1`;
|
|
explicit cast `long 1` to `short 1` -> `short 1`;
|
|
assign `short 1` value to `c`
|
|
<4> declare `double e`;
|
|
access `a` -> `int 1`;
|
|
explicit cast `int 1` to `double 1.0`;
|
|
assign `double 1.0` to `e`;
|
|
(note the explicit cast is extraneous since an implicit cast is valid)
|
|
+
|
|
* Invalid numeric type casts resulting in errors.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> int a = 1.0; // error
|
|
<2> int b = 2;
|
|
<3> byte c = b; // error
|
|
----
|
|
+
|
|
<1> declare `int i`;
|
|
*error* -> cannot implicit cast `double 1.0` to `int 1`;
|
|
(note an explicit cast is valid)
|
|
<2> declare `int b`;
|
|
assign `int 2` to `b`
|
|
<3> declare byte `c`;
|
|
access `b` -> `int 2`;
|
|
*error* -> cannot implicit cast `int 2` to `byte 2`;
|
|
(note an explicit cast is valid)
|
|
|
|
[[reference-type-casting]]
|
|
==== Reference Type Casting
|
|
|
|
A <<reference-types, reference type>> cast converts the value of an original
|
|
reference type to the equivalent value of a target reference type. An implicit
|
|
cast between two reference type values is allowed when the original reference
|
|
type is a descendant of the target type. An explicit cast between two reference
|
|
type values is allowed when the original type is a descendant of the target type
|
|
or the target type is a descendant of the original type.
|
|
|
|
*Examples*
|
|
|
|
* Valid reference type casts.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> List x;
|
|
<2> ArrayList y = new ArrayList();
|
|
<3> x = y;
|
|
<4> y = (ArrayList)x;
|
|
<5> x = (List)y;
|
|
----
|
|
+
|
|
<1> declare `List x`;
|
|
assign default value `null` to `x`
|
|
<2> declare `ArrayList y`;
|
|
allocate `ArrayList` instance -> `ArrayList reference`;
|
|
assign `ArrayList reference` to `y`;
|
|
<3> access `y` -> `ArrayList reference`;
|
|
implicit cast `ArrayList reference` to `List reference` -> `List reference`;
|
|
assign `List reference` to `x`;
|
|
(note `ArrayList` is a descendant of `List`)
|
|
<4> access `x` -> `List reference`;
|
|
explicit cast `List reference` to `ArrayList reference`
|
|
-> `ArrayList reference`;
|
|
assign `ArrayList reference` to `y`;
|
|
<5> access `y` -> `ArrayList reference`;
|
|
explicit cast `ArrayList reference` to `List reference` -> `List reference`;
|
|
assign `List reference` to `x`;
|
|
(note the explicit cast is extraneous, and an implicit cast is valid)
|
|
+
|
|
* Invalid reference type casts resulting in errors.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> List x = new ArrayList();
|
|
<2> ArrayList y = x; // error
|
|
<3> Map m = (Map)x; // error
|
|
----
|
|
+
|
|
<1> declare `List x`;
|
|
allocate `ArrayList` instance -> `ArrayList reference`;
|
|
implicit cast `ArrayList reference` to `List reference` -> `List reference`;
|
|
assign `List reference` to `x`
|
|
<2> declare `ArrayList y`;
|
|
access `x` -> `List reference`;
|
|
*error* -> cannot implicit cast `List reference` to `ArrayList reference`;
|
|
(note an explicit cast is valid since `ArrayList` is a descendant of `List`)
|
|
<3> declare `ArrayList y`;
|
|
access `x` -> `List reference`;
|
|
*error* -> cannot explicit cast `List reference` to `Map reference`;
|
|
(note no cast would be valid since neither `List` nor `Map` is a descendant
|
|
of the other)
|
|
|
|
[[dynamic-type-casting]]
|
|
==== Dynamic Type Casting
|
|
|
|
A <<dynamic-types, dynamic (`def`) type>> cast converts the value of an original
|
|
`def` type to the equivalent value of any target type or converts the value of
|
|
any original type to the equivalent value of a target `def` type.
|
|
|
|
An implicit cast from any original type value to a `def` type value is always
|
|
allowed. An explicit cast from any original type value to a `def` type value is
|
|
always allowed but never necessary.
|
|
|
|
An implicit or explicit cast from an original `def` type value to
|
|
any target type value is allowed if and only if the cast is normally allowed
|
|
based on the current type value the `def` type value represents.
|
|
|
|
*Examples*
|
|
|
|
* Valid dynamic type casts with any original type to a target `def` type.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> def d0 = 3;
|
|
<2> d0 = new ArrayList();
|
|
<3> Object o = new HashMap();
|
|
<4> def d1 = o;
|
|
<5> int i = d1.size();
|
|
----
|
|
+
|
|
<1> declare `def d0`;
|
|
implicit cast `int 3` to `def`;
|
|
assign `int 3` to `d0`
|
|
<2> allocate `ArrayList` instance -> `ArrayList reference`;
|
|
implicit cast `ArrayList reference` to `def` -> `def`;
|
|
assign `def` to `d0`
|
|
<3> declare `Object o`;
|
|
allocate `HashMap` instance -> `HashMap reference`;
|
|
implicit cast `HashMap reference` to `Object reference`
|
|
-> `Object reference`;
|
|
assign `Object reference` to `o`
|
|
<4> declare `def d1`;
|
|
access `o` -> `Object reference`;
|
|
implicit cast `Object reference` to `def` -> `def`;
|
|
assign `def` to `d1`
|
|
<5> declare `int i`;
|
|
access `d1` -> `def`;
|
|
implicit cast `def` to `HashMap reference` -> HashMap reference`;
|
|
call `size` on `HashMap reference` -> `int 0`;
|
|
assign `int 0` to `i`;
|
|
(note `def` was implicit cast to `HashMap reference` since `HashMap` is the
|
|
child-most descendant type value that the `def` type value
|
|
represents)
|
|
+
|
|
* Valid dynamic type casts with an original `def` type to any target type.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> def d = 1.0;
|
|
<2> int i = (int)d;
|
|
<3> d = 1;
|
|
<4> float f = d;
|
|
<5> d = new ArrayList();
|
|
<6> List l = d;
|
|
----
|
|
+
|
|
<1> declare `def d`;
|
|
implicit cast `double 1.0` to `def` -> `def`;
|
|
assign `def` to `d`
|
|
<2> declare `int i`;
|
|
access `d` -> `def`;
|
|
implicit cast `def` to `double 1.0` -> `double 1.0`;
|
|
explicit cast `double 1.0` to `int 1` -> `int 1`;
|
|
assign `int 1` to `i`;
|
|
(note the explicit cast is necessary since a `double` value cannot be
|
|
converted to an `int` value implicitly)
|
|
<3> assign `int 1` to `d`;
|
|
(note the switch in the type `d` represents from `double` to `int`)
|
|
<4> declare `float i`;
|
|
access `d` -> `def`;
|
|
implicit cast `def` to `int 1` -> `int 1`;
|
|
implicit cast `int 1` to `float 1.0` -> `float 1.0`;
|
|
assign `float 1.0` to `f`
|
|
<5> allocate `ArrayList` instance -> `ArrayList reference`;
|
|
assign `ArrayList reference` to `d`;
|
|
(note the switch in the type `d` represents from `int` to `ArrayList`)
|
|
<6> declare `List l`;
|
|
access `d` -> `def`;
|
|
implicit cast `def` to `ArrayList reference` -> `ArrayList reference`;
|
|
implicit cast `ArrayList reference` to `List reference` -> `List reference`;
|
|
assign `List reference` to `l`
|
|
+
|
|
* Invalid dynamic type casts resulting in errors.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> def d = 1;
|
|
<2> short s = d; // error
|
|
<3> d = new HashMap();
|
|
<4> List l = d; // error
|
|
----
|
|
<1> declare `def d`;
|
|
implicit cast `int 1` to `def` -> `def`;
|
|
assign `def` to `d`
|
|
<2> declare `short s`;
|
|
access `d` -> `def`;
|
|
implicit cast `def` to `int 1` -> `int 1`;
|
|
*error* -> cannot implicit cast `int 1` to `short 1`;
|
|
(note an explicit cast is valid)
|
|
<3> allocate `HashMap` instance -> `HashMap reference`;
|
|
implicit cast `HashMap reference` to `def` -> `def`;
|
|
assign `def` to `d`
|
|
<4> declare `List l`;
|
|
access `d` -> `def`;
|
|
implicit cast `def` to `HashMap reference`;
|
|
*error* -> cannot implicit cast `HashMap reference` to `List reference`;
|
|
(note no cast would be valid since neither `HashMap` nor `List` is a
|
|
descendant of the other)
|
|
|
|
[[string-character-casting]]
|
|
==== String to Character Casting
|
|
|
|
Use the *cast operator* to convert a <<string-type, `String` type>> value into a
|
|
<<primitive-types, `char` type>> value.
|
|
|
|
*Errors*
|
|
|
|
* If the `String` type value isn't one character in length.
|
|
* If the `String` type value is `null`.
|
|
|
|
*Examples*
|
|
|
|
* Casting string literals into `char` type values.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> char c = (char)"C"
|
|
<2> c = (char)'c'
|
|
----
|
|
+
|
|
<1> declare `char c`;
|
|
explicit cast `String "C"` to `char C` -> `char C`;
|
|
assign `char C` to `c`
|
|
<2> explicit cast `String 'c'` to `char c` -> `char c`;
|
|
assign `char c` to `c`
|
|
+
|
|
* Casting a `String` reference into a `char` value.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> String s = "s";
|
|
<2> char c = (char)s;
|
|
----
|
|
<1> declare `String s`;
|
|
assign `String "s"` to `s`;
|
|
<2> declare `char c`
|
|
access `s` -> `String "s"`;
|
|
explicit cast `String "s"` to `char s` -> `char s`;
|
|
assign `char s` to `c`
|
|
|
|
[[boxing-unboxing]]
|
|
==== Boxing and Unboxing
|
|
|
|
Boxing is a special type of cast used to convert a primitive type to its
|
|
corresponding reference type. Unboxing is the reverse used to convert a
|
|
reference type to its corresponding primitive type.
|
|
|
|
Implicit boxing/unboxing occurs during the following operations:
|
|
|
|
* Conversions between a `def` type and a primitive type will be implicitly
|
|
boxed/unboxed as necessary, though this is referred to as an implicit cast
|
|
throughout the documentation.
|
|
* Method/function call arguments will be implicitly boxed/unboxed as necessary.
|
|
* A primitive type value will be implicitly boxed when a reference type method
|
|
call is invoked on it.
|
|
|
|
Explicit boxing/unboxing is not allowed. Use the reference type API to
|
|
explicitly convert a primitive type value to its respective reference type
|
|
value and vice versa.
|
|
|
|
*Errors*
|
|
|
|
* If an explicit cast is made to box/unbox a primitive type.
|
|
|
|
*Examples*
|
|
|
|
* Uses of implicit boxing/unboxing.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> List l = new ArrayList();
|
|
<2> l.add(1);
|
|
<3> Integer I = Integer.valueOf(0);
|
|
<4> int i = l.get(i);
|
|
----
|
|
+
|
|
<1> declare `List l`;
|
|
allocate `ArrayList` instance -> `ArrayList reference`;
|
|
assign `ArrayList reference` to `l`;
|
|
<2> access `l` -> `List reference`;
|
|
implicit cast `int 1` to `def` -> `def`;
|
|
call `add` on `List reference` with arguments (`def`);
|
|
(note internally `int 1` is boxed to `Integer 1` to store as a `def` type
|
|
value)
|
|
<3> declare `Integer I`;
|
|
call `valueOf` on `Integer` with arguments of (`int 0`) -> `Integer 0`;
|
|
assign `Integer 0` to `I`;
|
|
<4> declare `int i`;
|
|
access `I` -> `Integer 0`;
|
|
unbox `Integer 0` -> `int 0`;
|
|
access `l` -> `List reference`;
|
|
call `get` on `List reference` with arguments (`int 0`) -> `def`;
|
|
implicit cast `def` to `int 1` -> `int 1`;
|
|
assign `int 1` to `i`;
|
|
(note internally `int 1` is unboxed from `Integer 1` when loaded from a
|
|
`def` type value)
|
|
+
|
|
* Uses of invalid boxing/unboxing resulting in errors.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> Integer x = 1; // error
|
|
<2> Integer y = (Integer)1; // error
|
|
<3> int a = Integer.valueOf(1); // error
|
|
<4> int b = (int)Integer.valueOf(1); // error
|
|
----
|
|
+
|
|
<1> declare `Integer x`;
|
|
*error* -> cannot implicit box `int 1` to `Integer 1` during assignment
|
|
<2> declare `Integer y`;
|
|
*error* -> cannot explicit box `int 1` to `Integer 1` during assignment
|
|
<3> declare `int a`;
|
|
call `valueOf` on `Integer` with arguments of (`int 1`) -> `Integer 1`;
|
|
*error* -> cannot implicit unbox `Integer 1` to `int 1` during assignment
|
|
<4> declare `int a`;
|
|
call `valueOf` on `Integer` with arguments of (`int 1`) -> `Integer 1`;
|
|
*error* -> cannot explicit unbox `Integer 1` to `int 1` during assignment
|
|
|
|
[[promotion]]
|
|
==== Promotion
|
|
|
|
Promotion is when a single value is implicitly cast to a certain type or
|
|
multiple values are implicitly cast to the same type as required for evaluation
|
|
by certain operations. Each operation that requires promotion has a promotion
|
|
table that shows all required implicit casts based on the type(s) of value(s). A
|
|
value can be promoted to a `def` type at compile-time; however, the promoted
|
|
type value is derived from what the `def` type value represents at run-time.
|
|
|
|
*Errors*
|
|
|
|
* If a specific operation cannot find an allowed promotion type for the type(s)
|
|
of value(s) given.
|
|
|
|
*Examples*
|
|
|
|
* Uses of promotion.
|
|
+
|
|
[source,Painless]
|
|
----
|
|
<1> double d = 2 + 2.0;
|
|
<2> def x = 1;
|
|
<3> float f = x + 2.0F;
|
|
----
|
|
<1> declare `double d`;
|
|
promote `int 2` and `double 2.0 @0` -> `double 2.0 @0`;
|
|
implicit cast `int 2` to `double 2.0 @1` -> `double 2.0 @1`;
|
|
add `double 2.0 @1` and `double 2.0 @0` -> `double 4.0`;
|
|
assign `double 4.0` to `d`
|
|
<2> declare `def x`;
|
|
implicit cast `int 1` to `def` -> `def`;
|
|
assign `def` to `x`;
|
|
<3> declare `float f`;
|
|
access `x` -> `def`;
|
|
implicit cast `def` to `int 1` -> `int 1`;
|
|
promote `int 1` and `float 2.0` -> `float 2.0`;
|
|
implicit cast `int 1` to `float 1.0` -> `float `1.0`;
|
|
add `float 1.0` and `float 2.0` -> `float 3.0`;
|
|
assign `float 3.0` to `f`;
|
|
(note this example illustrates promotion done at run-time as promotion
|
|
done at compile-time would have resolved to a `def` type value)
|