[[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 <>. An explicit cast specifies the target type and forcefully occurs as its own operation. Use the `cast operator '()'` to specify an explicit cast. Refer to the <> for a quick reference on all allowed casts. *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`; store `int 5` to `i` <2> declare `Map m`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `m` <3> declare `HashMap hm`; load from `m` -> `Map reference`; explicit cast `Map reference` to `HashMap reference` -> `HashMap reference`; store `HashMap reference` to `hm` [[numeric-type-casting]] ==== Numeric Type Casting A <> 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`; store `int 1` to `a` <2> declare `long b`; load from `a` -> `int 1`; implicit cast `int 1` to `long 1` -> `long 1`; store `long 1` to `b` <3> declare `short c`; load from `b` -> `long 1`; explicit cast `long 1` to `short 1` -> `short 1`; store `short 1` value to `c` <4> declare `double e`; load from `a` -> `int 1`; explicit cast `int 1` to `double 1.0`; store `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`; store `int 2` to `b` <3> declare byte `c`; load from `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 <> 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`; store default value `null` to `x` <2> declare `ArrayList y`; allocate `ArrayList` instance -> `ArrayList reference`; store `ArrayList reference` to `y`; <3> load from `y` -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `x`; (note `ArrayList` is a descendant of `List`) <4> load from `x` -> `List reference`; explicit cast `List reference` to `ArrayList reference` -> `ArrayList reference`; store `ArrayList reference` to `y`; <5> load from `y` -> `ArrayList reference`; explicit cast `ArrayList reference` to `List reference` -> `List reference`; store `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`; store `List reference` to `x` <2> declare `ArrayList y`; load from `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`; load from `x` -> `List reference`; *error* -> cannot explicit cast `List reference` to `Map reference`; (note no cast is valid since neither `List` nor `Map` is a descendant of the other) [[dynamic-type-casting]] ==== Dynamic Type Casting A <> 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`; store `int 3` to `d0` <2> allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `def` -> `def`; store `def` to `d0` <3> declare `Object o`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `Object reference` -> `Object reference`; store `Object reference` to `o` <4> declare `def d1`; load from `o` -> `Object reference`; implicit cast `Object reference` to `def` -> `def`; store `def` to `d1` <5> declare `int i`; load from `d1` -> `def`; implicit cast `def` to `HashMap reference` -> HashMap reference`; call `size` on `HashMap reference` -> `int 0`; store `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`; store `def` to `d` <2> declare `int i`; load from `d` -> `def`; implicit cast `def` to `double 1.0` -> `double 1.0`; explicit cast `double 1.0` to `int 1` -> `int 1`; store `int 1` to `i`; (note the explicit cast is necessary since a `double` type value is not converted to an `int` type value implicitly) <3> store `int 1` to `d`; (note the switch in the type `d` represents from `double` to `int`) <4> declare `float i`; load from `d` -> `def`; implicit cast `def` to `int 1` -> `int 1`; implicit cast `int 1` to `float 1.0` -> `float 1.0`; store `float 1.0` to `f` <5> allocate `ArrayList` instance -> `ArrayList reference`; store `ArrayList reference` to `d`; (note the switch in the type `d` represents from `int` to `ArrayList`) <6> declare `List l`; load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `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`; store `def` to `d` <2> declare `short s`; load from `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`; store `def` to `d` <4> declare `List l`; load from `d` -> `def`; implicit cast `def` to `HashMap reference`; *error* -> cannot implicit cast `HashMap reference` to `List reference`; (note no cast is 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 <> value into a <> 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`; store `char C` to `c` <2> explicit cast `String 'c'` to `char c` -> `char c`; store `char c` to `c` + * Casting a `String` reference into a `char` type value. + [source,Painless] ---- <1> String s = "s"; <2> char c = (char)s; ---- <1> declare `String s`; store `String "s"` to `s`; <2> declare `char c` load from `s` -> `String "s"`; explicit cast `String "s"` to `char s` -> `char s`; store `char s` to `c` [[character-string-casting]] ==== Character to String Casting Use the cast operator to convert a <> value into a <> value. *Examples* * Casting a `String` reference into a `char` type value. + [source,Painless] ---- <1> char c = 65; <2> String s = (String)c; ---- <1> declare `char c`; store `char 65` to `c`; <2> declare `String s` load from `c` -> `char A`; explicit cast `char A` to `String "A"` -> `String "A"`; store `String "A"` to `s` [[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 are implicitly boxed/unboxed as necessary, though this is referred to as an implicit cast throughout the documentation. * Method/function call arguments are implicitly boxed/unboxed as necessary. * A primitive type value is implicitly boxed when a reference type method is called 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`; store `ArrayList reference` to `l`; <2> load from `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`; store `Integer 0` to `I`; <4> declare `int i`; load from `I` -> `Integer 0`; unbox `Integer 0` -> `int 0`; load from `l` -> `List reference`; call `get` on `List reference` with arguments (`int 0`) -> `def`; implicit cast `def` to `int 1` -> `int 1`; store `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 promoted to a `def` type at compile-time is promoted again at run-time based on the type the `def` value represents. *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`: result `double`; 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`; store `double 4.0` to `d` <2> declare `def x`; implicit cast `int 1` to `def` -> `def`; store `def` to `x`; <3> declare `float f`; load from `x` -> `def`; implicit cast `def` to `int 1` -> `int 1`; promote `int 1` and `float 2.0`: result `float`; implicit cast `int 1` to `float 1.0` -> `float `1.0`; add `float 1.0` and `float 2.0` -> `float 3.0`; store `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) [[allowed-casts]] ==== Allowed Casts The following tables show all allowed casts. Read the tables row by row, where the original type is shown in the first column, and each subsequent column indicates whether a cast to the specified target type is implicit (I), explicit (E), boxed/unboxed for methods only (A), a reference type cast (@), or is not allowed (-). See <> for allowed reference type casts. *Primitive/Reference Types* [cols="<3,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1"] |==== | | O | N | T | b | y | s | c | i | j | f | d | B | Y | S | C | I | J | F | D | R | def | Object ( O ) | | @ | @ | - | - | - | - | - | - | - | - | @ | @ | @ | @ | @ | @ | @ | @ | @ | I | Number ( N ) | I | | - | - | - | - | - | - | - | - | - | - | @ | @ | - | @ | @ | @ | @ | @ | I | String ( T ) | I | - | | - | - | - | - | - | - | - | - | - | - | - | E | - | - | - | - | - | I | boolean ( b ) | A | - | - | | - | - | - | - | - | - | - | A | - | - | - | - | - | - | - | - | I | byte ( y ) | A | A | - | - | | I | E | I | I | I | I | - | A | A | - | A | A | A | A | - | I | short ( s ) | A | A | - | - | E | | E | I | I | I | I | - | - | A | - | A | A | A | A | - | I | char ( c ) | A | - | E | - | E | E | | I | I | I | I | - | - | - | A | A | A | A | A | - | I | int ( i ) | A | A | - | - | E | E | E | | I | I | I | - | - | - | - | A | A | A | A | - | I | long ( j ) | A | A | - | - | E | E | E | E | | I | I | - | - | - | - | - | A | A | A | - | I | float ( f ) | A | A | - | - | E | E | E | E | E | | I | - | - | - | - | - | - | A | A | - | I | double ( d ) | A | A | - | - | E | E | E | E | E | E | | - | - | - | - | - | - | - | A | - | I | Boolean ( B ) | A | - | - | A | - | - | - | - | - | - | - | | - | - | - | - | - | - | - | @ | I | Byte ( Y ) | A | I | - | - | A | A | - | A | A | A | A | - | | A | - | A | A | A | A | @ | I | Short ( S ) | A | I | - | - | - | A | - | A | A | A | A | - | - | | - | A | A | A | A | @ | I | Character ( C ) | A | - | - | - | - | - | A | A | A | A | A | - | - | - | | A | A | A | A | @ | I | Integer ( I ) | A | - | - | - | - | - | - | A | A | A | A | - | - | - | - | | A | A | A | @ | I | Long ( J ) | A | - | - | - | - | - | - | - | A | A | A | - | - | - | - | - | | A | A | @ | I | Float ( F ) | A | - | - | - | - | - | - | - | - | A | A | - | - | - | - | - | - | | A | @ | I | Double ( D ) | A | - | - | - | - | - | - | - | - | - | A | - | - | - | - | - | - | - | | @ | I | Reference ( R ) | I | @ | @ | - | - | - | - | - | - | - | - | @ | @ | @ | @ | @ | @ | @ | @ | @ | I |==== *`def` Type* [cols="<3,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1,^1"] |==== | | O | N | T | b | y | s | c | i | j | f | d | B | Y | S | C | I | J | F | D | R | def as String | I | - | I | - | - | - | E | - | - | - | - | - | - | - | E | - | - | - | - | @ | def as boolean/Boolean | I | - | - | I | - | - | - | - | - | - | - | I | - | - | - | - | - | - | - | @ | def as byte/Byte | I | - | - | - | I | I | E | I | I | I | I | - | I | I | E | I | I | I | I | @ | def as short/Short | I | - | - | - | E | I | E | I | I | I | I | - | E | I | E | I | I | I | I | @ | def as char/Character | I | - | - | - | E | E | I | I | I | I | I | - | E | E | I | I | I | I | I | @ | def as int/Integer | I | - | - | - | E | E | E | I | I | I | I | - | E | E | E | I | I | I | I | @ | def as long/Long | I | - | - | - | E | E | E | E | I | I | I | - | E | E | E | E | I | I | I | @ | def as float/Float | I | - | - | - | E | E | E | E | E | I | I | - | E | E | E | E | E | I | I | @ | def as double/Double | I | - | - | - | E | E | E | E | E | E | I | - | E | E | E | E | E | E | I | @ | def as Reference | @ | @ | @ | - | - | - | - | - | - | - | - | @ | @ | @ | @ | @ | @ | @ | @ | @ |====