OpenSearch/docs/painless/painless-casting.asciidoc

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)