[[painless-operators-reference]] === Operators: Reference [[method-call-operator]] ==== Method Call Use the `method call operator '()'` to call a member method on a <<reference-types, reference type>> value. Implicit <<boxing-unboxing, boxing/unboxing>> is evaluated as necessary per argument during the method call. When a method call is made on a target `def` type value, the parameters and return type value are considered to also be of the `def` type and are evaluated at run-time. An overloaded method is one that shares the same name with two or more methods. A method is overloaded based on arity where the same name is re-used for multiple methods as long as the number of parameters differs. *Errors* * If the reference type value is `null`. * If the member method name doesn't exist for a given reference type value. * If the number of arguments passed in is different from the number of specified parameters. * If the arguments cannot be implicitly cast or implicitly boxed/unboxed to the correct type values for the parameters. *Grammar* [source,ANTLR4] ---- method_call: '.' ID arguments; arguments: '(' (expression (',' expression)*)? ')'; ---- *Examples* * Method calls on different reference types. + [source,Painless] ---- Map m = new HashMap(); <1> m.put(1, 2); <2> int z = m.get(1); <3> def d = new ArrayList(); <4> d.add(1); <5> int i = Integer.parseInt(d.get(0).toString()); <6> ---- + <1> declare `Map m`; allocate `HashMap` instance -> `HashMap reference`; store `HashMap reference` to `m` <2> load from `m` -> `Map reference`; implicit cast `int 1` to `def` -> `def`; implicit cast `int 2` to `def` -> `def`; call `put` on `Map reference` with arguments (`int 1`, `int 2`) <3> declare `int z`; load from `m` -> `Map reference`; call `get` on `Map reference` with arguments (`int 1`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; store `int 2` to `z` <4> declare `def d`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList` to `def` -> `def`; store `def` to `d` <5> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference` call `add` on `ArrayList reference` with arguments (`int 1`); <6> declare `int i`; load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference` call `get` on `ArrayList reference` with arguments (`int 1`) -> `def`; implicit cast `def` to `Integer 1 reference` -> `Integer 1 reference`; call `toString` on `Integer 1 reference` -> `String '1'`; call `parseInt` on `Integer` with arguments (`String '1'`) -> `int 1`; store `int 1` in `i`; [[field-access-operator]] ==== Field Access Use the `field access operator '.'` to store a value to or load a value from a <<reference-types, reference type>> member field. *Errors* * If the reference type value is `null`. * If the member field name doesn't exist for a given reference type value. *Grammar* [source,ANTLR4] ---- field_access: '.' ID; ---- *Examples* The examples use the following reference type definition: [source,Painless] ---- name: Example non-static member fields: * int x * def y * List z ---- * Field access with the `Example` type. + [source,Painless] ---- Example example = new Example(); <1> example.x = 1; <2> example.y = example.x; <3> example.z = new ArrayList(); <4> example.z.add(1); <5> example.x = example.z.get(0); <6> ---- + <1> declare `Example example`; allocate `Example` instance -> `Example reference`; store `Example reference` to `example` <2> load from `example` -> `Example reference`; store `int 1` to `x` of `Example reference` <3> load from `example` -> `Example reference @0`; load from `example` -> `Example reference @1`; load from `x` of `Example reference @1` -> `int 1`; implicit cast `int 1` to `def` -> `def`; store `def` to `y` of `Example reference @0`; (note `Example reference @0` and `Example reference @1` are the same) <4> load from `example` -> `Example reference`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `z` of `Example reference` <5> load from `example` -> `Example reference`; load from `z` of `Example reference` -> `List reference`; call `add` on `List reference` with arguments (`int 1`) <6> load from `example` -> `Example reference @0`; load from `example` -> `Example reference @1`; load from `z` of `Example reference @1` -> `List reference`; call `get` on `List reference` with arguments (`int 0`) -> `int 1`; store `int 1` in `x` of `List reference @0`; (note `Example reference @0` and `Example reference @1` are the same) [[null-safe-operator]] ==== Null Safe Use the `null safe operator '?.'` instead of the method call operator or field access operator to ensure a reference type value is `non-null` before a method call or field access. A `null` value will be returned if the reference type value is `null`, otherwise the method call or field access is evaluated. *Errors* * If the method call return type value or the field access type value is not a reference type value and is not implicitly castable to a reference type value. *Grammar* [source,ANTLR4] ---- null_safe: null_safe_method_call | null_safe_field_access ; null_safe_method_call: '?.' ID arguments; arguments: '(' (expression (',' expression)*)? ')'; null_safe_field_access: '?.' ID; ---- *Examples* The examples use the following reference type definition: [source,Painless] ---- name: Example non-static member methods: * List factory() non-static member fields: * List x ---- * Null safe without a `null` value. + [source,Painless] ---- Example example = new Example(); <1> List x = example?.factory(); <2> ---- + <1> declare `Example example`; allocate `Example` instance -> `Example reference`; store `Example reference` to `example` <2> declare `List x`; load from `example` -> `Example reference`; null safe call `factory` on `Example reference` -> `List reference`; store `List reference` to `x`; + * Null safe with a `null` value; + [source,Painless] ---- Example example = null; <1> List x = example?.x; <2> ---- <1> declare `Example example`; store `null` to `example` <2> declare `List x`; load from `example` -> `Example reference`; null safe access `x` on `Example reference` -> `null`; store `null` to `x`; (note the *null safe operator* returned `null` because `example` is `null`) [[list-initialization-operator]] ==== List Initialization Use the `list initialization operator '[]'` to allocate an `List` type instance to the heap with a set of pre-defined values. Each value used to initialize the `List` type instance is cast to a `def` type value upon insertion into the `List` type instance using the `add` method. The order of the specified values is maintained. *Grammar* [source,ANTLR4] ---- list_initialization: '[' expression (',' expression)* ']' | '[' ']'; ---- *Examples* * List initialization of an empty `List` type value. + [source,Painless] ---- List empty = []; <1> ---- + <1> declare `List empty`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `empty` + * List initialization with static values. + [source,Painless] ---- List list = [1, 2, 3]; <1> ---- + <1> declare `List list`; allocate `ArrayList` instance -> `ArrayList reference`; call `add` on `ArrayList reference` with arguments(`int 1`); call `add` on `ArrayList reference` with arguments(`int 2`); call `add` on `ArrayList reference` with arguments(`int 3`); implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `list` + * List initialization with non-static values. + [source,Painless] ---- int i = 1; <1> long l = 2L; <2> float f = 3.0F; <3> double d = 4.0; <4> String s = "5"; <5> List list = [i, l, f*d, s]; <6> ---- + <1> declare `int i`; store `int 1` to `i` <2> declare `long l`; store `long 2` to `l` <3> declare `float f`; store `float 3.0` to `f` <4> declare `double d`; store `double 4.0` to `d` <5> declare `String s`; store `String "5"` to `s` <6> declare `List list`; allocate `ArrayList` instance -> `ArrayList reference`; load from `i` -> `int 1`; call `add` on `ArrayList reference` with arguments(`int 1`); load from `l` -> `long 2`; call `add` on `ArrayList reference` with arguments(`long 2`); load from `f` -> `float 3.0`; load from `d` -> `double 4.0`; promote `float 3.0` and `double 4.0`: result `double`; implicit cast `float 3.0` to `double 3.0` -> `double 3.0`; multiply `double 3.0` and `double 4.0` -> `double 12.0`; call `add` on `ArrayList reference` with arguments(`double 12.0`); load from `s` -> `String "5"`; call `add` on `ArrayList reference` with arguments(`String "5"`); implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `list` [[list-access-operator]] ==== List Access Use the `list access operator '[]'` as a shortcut for a `set` method call or `get` method call made on a `List` type value. *Errors* * If a value other than a `List` type value is accessed. * If a non-integer type value is used as an index for a `set` method call or `get` method call. *Grammar* [source,ANTLR4] ---- list_access: '[' expression ']' ---- *Examples* * List access with the `List` type. + [source,Painless] ---- List list = new ArrayList(); <1> list.add(1); <2> list.add(2); <3> list.add(3); <4> list[0] = 2; <5> list[1] = 5; <6> int x = list[0] + list[1]; <7> int y = 1; <8> int z = list[y]; <9> ---- + <1> declare `List list`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `list` <2> load from `list` -> `List reference`; call `add` on `List reference` with arguments(`int 1`) <3> load from `list` -> `List reference`; call `add` on `List reference` with arguments(`int 2`) <4> load from `list` -> `List reference`; call `add` on `List reference` with arguments(`int 3`) <5> load from `list` -> `List reference`; call `set` on `List reference` with arguments(`int 0`, `int 2`) <6> load from `list` -> `List reference`; call `set` on `List reference` with arguments(`int 1`, `int 5`) <7> declare `int x`; load from `list` -> `List reference`; call `get` on `List reference` with arguments(`int 0`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; load from `list` -> `List reference`; call `get` on `List reference` with arguments(`int 1`) -> `def`; implicit cast `def` to `int 5` -> `int 5`; add `int 2` and `int 5` -> `int 7`; store `int 7` to `x` <8> declare `int y`; store `int 1` int `y` <9> declare `int z`; load from `list` -> `List reference`; load from `y` -> `int 1`; call `get` on `List reference` with arguments(`int 1`) -> `def`; implicit cast `def` to `int 5` -> `int 5`; store `int 5` to `z` + * List access with the `def` type. + [source,Painless] ---- def d = new ArrayList(); <1> d.add(1); <2> d.add(2); <3> d.add(3); <4> d[0] = 2; <5> d[1] = 5; <6> def x = d[0] + d[1]; <7> def y = 1; <8> def z = d[y]; <9> ---- + <1> declare `List d`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `def` -> `def`; store `def` to `d` <2> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `add` on `ArrayList reference` with arguments(`int 1`) <3> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `add` on `ArrayList reference` with arguments(`int 2`) <4> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `add` on `ArrayList reference` with arguments(`int 3`) <5> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `set` on `ArrayList reference` with arguments(`int 0`, `int 2`) <6> load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `set` on `ArrayList reference` with arguments(`int 1`, `int 5`) <7> declare `def x`; load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `get` on `ArrayList reference` with arguments(`int 0`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; load from `d` -> `def`; implicit cast `def` to `ArrayList reference` -> `ArrayList reference`; call `get` on `ArrayList reference` with arguments(`int 1`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; add `int 2` and `int 5` -> `int 7`; store `int 7` to `x` <8> declare `int y`; store `int 1` int `y` <9> declare `int z`; load from `d` -> `ArrayList reference`; load from `y` -> `def`; implicit cast `def` to `int 1` -> `int 1`; call `get` on `ArrayList reference` with arguments(`int 1`) -> `def`; store `def` to `z` [[map-initialization-operator]] ==== Map Initialization Use the `map initialization operator '[:]'` to allocate a `Map` type instance to the heap with a set of pre-defined values. Each pair of values used to initialize the `Map` type instance are cast to `def` type values upon insertion into the `Map` type instance using the `put` method. *Grammar* [source,ANTLR4] ---- map_initialization: '[' key_pair (',' key_pair)* ']' | '[' ':' ']'; key_pair: expression ':' expression ---- *Examples* * Map initialization of an empty `Map` type value. + [source,Painless] ---- Map empty = [:]; <1> ---- + <1> declare `Map empty`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `empty` + * Map initialization with static values. + [source,Painless] ---- Map map = [1:2, 3:4, 5:6]; <1> ---- + <1> declare `Map map`; allocate `HashMap` instance -> `HashMap reference`; call `put` on `HashMap reference` with arguments(`int 1`, `int 2`); call `put` on `HashMap reference` with arguments(`int 3`, `int 4`); call `put` on `HashMap reference` with arguments(`int 5`, `int 6`); implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `map` + * Map initialization with non-static values. + [source,Painless] ---- byte b = 0; <1> int i = 1; <2> long l = 2L; <3> float f = 3.0F; <4> double d = 4.0; <5> String s = "5"; <6> Map map = [b:i, l:f*d, d:s]; <7> ---- + <1> declare `byte b`; store `byte 0` to `b` <2> declare `int i`; store `int 1` to `i` <3> declare `long l`; store `long 2` to `l` <4> declare `float f`; store `float 3.0` to `f` <5> declare `double d`; store `double 4.0` to `d` <6> declare `String s`; store `String "5"` to `s` <7> declare `Map map`; allocate `HashMap` instance -> `HashMap reference`; load from `b` -> `byte 0`; load from `i` -> `int 1`; call `put` on `HashMap reference` with arguments(`byte 0`, `int 1`); load from `l` -> `long 2`; load from `f` -> `float 3.0`; load from `d` -> `double 4.0`; promote `float 3.0` and `double 4.0`: result `double`; implicit cast `float 3.0` to `double 3.0` -> `double 3.0`; multiply `double 3.0` and `double 4.0` -> `double 12.0`; call `put` on `HashMap reference` with arguments(`long 2`, `double 12.0`); load from `d` -> `double 4.0`; load from `s` -> `String "5"`; call `put` on `HashMap reference` with arguments(`double 4.0`, `String "5"`); implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `map` [[map-access-operator]] ==== Map Access Use the `map access operator '[]'` as a shortcut for a `put` method call or `get` method call made on a `Map` type value. *Errors* * If a value other than a `Map` type value is accessed. *Grammar* [source,ANTLR4] ---- map_access: '[' expression ']' ---- *Examples* * Map access with the `Map` type. + [source,Painless] ---- Map map = new HashMap(); <1> map['value2'] = 2; <2> map['value5'] = 5; <3> int x = map['value2'] + map['value5']; <4> String y = 'value5'; <5> int z = x[z]; <6> ---- + <1> declare `Map map`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `map` <2> load from `map` -> `Map reference`; call `put` on `Map reference` with arguments(`String 'value2'`, `int 2`) <3> load from `map` -> `Map reference`; call `put` on `Map reference` with arguments(`String 'value5'`, `int 5`) <4> declare `int x`; load from `map` -> `Map reference`; call `get` on `Map reference` with arguments(`String 'value2'`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; load from `map` -> `Map reference`; call `get` on `Map reference` with arguments(`String 'value5'`) -> `def`; implicit cast `def` to `int 5` -> `int 5`; add `int 2` and `int 5` -> `int 7`; store `int 7` to `x` <5> declare `String y`; store `String 'value5'` to `y` <6> declare `int z`; load from `map` -> `Map reference`; load from `y` -> `String 'value5'`; call `get` on `Map reference` with arguments(`String 'value5'`) -> `def`; implicit cast `def` to `int 5` -> `int 5`; store `int 5` to `z` + * Map access with the `def` type. + [source,Painless] ---- def d = new HashMap(); <1> d['value2'] = 2; <2> d['value5'] = 5; <3> int x = d['value2'] + d['value5']; <4> String y = 'value5'; <5> def z = d[y]; <6> ---- + <1> declare `def d`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `def` -> `def`; store `def` to `d` <2> load from `d` -> `def`; implicit cast `def` to `HashMap reference` -> `HashMap reference`; call `put` on `HashMap reference` with arguments(`String 'value2'`, `int 2`) <3> load from `d` -> `def`; implicit cast `def` to `HashMap reference` -> `HashMap reference`; call `put` on `HashMap reference` with arguments(`String 'value5'`, `int 5`) <4> declare `int x`; load from `d` -> `def`; implicit cast `def` to `HashMap reference` -> `HashMap reference`; call `get` on `HashMap reference` with arguments(`String 'value2'`) -> `def`; implicit cast `def` to `int 2` -> `int 2`; load from `d` -> `def`; call `get` on `HashMap reference` with arguments(`String 'value5'`) -> `def`; implicit cast `def` to `int 5` -> `int 5`; add `int 2` and `int 5` -> `int 7`; store `int 7` to `x` <5> declare `String y`; store `String 'value5'` to `y` <6> declare `def z`; load from `d` -> `def`; load from `y` -> `String 'value5'`; call `get` on `HashMap reference` with arguments(`String 'value5'`) -> `def`; store `def` to `z` [[new-instance-operator]] ==== New Instance Use the `new instance operator 'new ()'` to allocate a <<reference-types, reference type>> instance to the heap and call a specified constructor. Implicit <<boxing-unboxing, boxing/unboxing>> is evaluated as necessary per argument during the constructor call. An overloaded constructor is one that shares the same name with two or more constructors. A constructor is overloaded based on arity where the same reference type name is re-used for multiple constructors as long as the number of parameters differs. *Errors* * If the reference type name doesn't exist for instance allocation. * If the number of arguments passed in is different from the number of specified parameters. * If the arguments cannot be implicitly cast or implicitly boxed/unboxed to the correct type values for the parameters. *Grammar* [source,ANTLR4] ---- new_instance: 'new' TYPE '(' (expression (',' expression)*)? ')'; ---- *Examples* * Allocation of new instances with different types. [source,Painless] ---- Map m = new HashMap(); <1> def d = new ArrayList(); <2> def e = new HashMap(m); <3> ---- <1> declare `Map m`; allocate `HashMap` instance -> `HashMap reference`; implicit cast `HashMap reference` to `Map reference` -> `Map reference`; store `Map reference` to `m`; <2> declare `def d`; allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `def` -> `def`; store `def` to `d`; <3> declare `def e`; load from `m` -> `Map reference`; allocate `HashMap` instance with arguments (`Map reference`) -> `HashMap reference`; implicit cast `HashMap reference` to `def` -> `def`; store `def` to `e`; [[string-concatenation-operator]] ==== String Concatenation Use the `string concatenation operator '+'` to concatenate two values together where at least one of the values is a <<string-type, `String` type>>. *Grammar* [source,ANTLR4] ---- concatenate: expression '+' expression; ---- *Examples* * String concatenation with different primitive types. + [source,Painless] ---- String x = "con"; <1> String y = x + "cat"; <2> String z = 4 + 5 + x; <3> ---- + <1> declare `String x`; store `String "con"` to `x`; <2> declare `String y`; load from `x` -> `String "con"`; concat `String "con"` and `String "cat"` -> `String "concat"`; store `String "concat"` to `y` <3> declare `String z`; add `int 4` and `int 5` -> `int 9`; concat `int 9` and `String "9concat"`; store `String "9concat"` to `z`; (note the addition is done prior to the concatenation due to precedence and associativity of the specific operations) + * String concatenation with the `def` type. + [source,Painless] ---- def d = 2; <1> d = "con" + d + "cat"; <2> ---- + <1> declare `def`; implicit cast `int 2` to `def` -> `def`; store `def` in `d`; <2> concat `String "con"` and `int 9` -> `String "con9"`; concat `String "con9"` and `String "con"` -> `String "con9cat"` implicit cast `String "con9cat"` to `def` -> `def`; store `def` to `d`; (note the switch in type of `d` from `int` to `String`) [[elvis-operator]] ==== Elvis An elvis consists of two expressions. The first expression is evaluated with to check for a `null` value. If the first expression evaluates to `null` then the second expression is evaluated and its value used. If the first expression evaluates to `non-null` then the resultant value of the first expression is used. Use the `elvis operator '?:'` as a shortcut for the conditional operator. *Errors* * If the first expression or second expression cannot produce a `null` value. *Grammar* [source,ANTLR4] ---- elvis: expression '?:' expression; ---- *Examples* * Elvis with different reference types. + [source,Painless] ---- List x = new ArrayList(); <1> List y = x ?: new ArrayList(); <2> y = null; <3> List z = y ?: new ArrayList(); <4> ---- + <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 `List y`; load `x` -> `List reference`; `List reference` equals `null` -> `false`; evaluate 1st expression: `List reference` -> `List reference`; store `List reference` to `y` <3> store `null` to `y`; <4> declare `List z`; load `y` -> `List reference`; `List reference` equals `null` -> `true`; evaluate 2nd expression: allocate `ArrayList` instance -> `ArrayList reference`; implicit cast `ArrayList reference` to `List reference` -> `List reference`; store `List reference` to `z`;