OpenSearch/docs/painless/painless-types.asciidoc

467 lines
16 KiB
Plaintext

[[painless-types]]
=== Types
A type is a classification of data used to define the properties of a value.
These properties specify what data a value represents and the rules for how a
value is evaluated during an <<painless-operators, operation>>. Each type
belongs to one of the following categories: <<primitive-types, primitive>>,
<<reference-types, reference>>, or <<dynamic-types, dynamic>>.
[[primitive-types]]
==== Primitive Types
A primitive type represents basic data built natively into the JVM and is
allocated to non-heap memory. Declare a primitive type
<<painless-variables, variable>>, and assign it a primitive type value for
evaluation during later operations. The default value for a newly-declared
primitive type variable is listed as part of the definitions below. A primitive
type value is copied during an assignment or as an argument for a
method/function call.
A primitive type has a corresponding reference type (also known as a boxed
type). Use the <<field-access, field access operator>> or
<<method-access, method call operator>> on a primitive type value to force
evaluation as its corresponding reference type value.
The following primitive types are available:
[horizontal]
`byte`::
8-bit, signed, two's complement integer
* range: [`-128`, `127`]
* default value: `0`
* reference type: `Byte`
`short`::
16-bit, signed, two's complement integer
* range: [`-32768`, `32767`]
* default value: `0`
* reference type: `Short`
`char`::
16-bit, unsigned, Unicode character
* range: [`0`, `65535`]
* default value: `0` or `\u0000`
* reference type: `Character`
`int`::
32-bit, signed, two's complement integer
* range: [`-2^32`, `2^32-1`]
* default value: `0`
* reference type: `Integer`
`long`::
64-bit, signed, two's complement integer
* range: [`-2^64`, `2^64-1`]
* default value: `0`
* reference type: `Long`
`float`::
32-bit, signed, single-precision, IEEE 754 floating point number
* default value: `0.0`
* reference type: `Float`
`double`::
64-bit, signed, double-precision, IEEE 754 floating point number
* default value: `0.0`
* reference type: `Double`
`boolean`::
logical quantity with two possible values of `true` and `false`
* default value: `false`
* reference type: `Boolean`
*Examples*
* Primitive types used in declaration, declaration and assignment.
+
[source,Painless]
----
<1> int i = 1;
<2> double d;
<3> boolean b = true;
----
+
<1> declare `int i`;
assign `int 1` to `i`
<2> declare `double d`;
assign default `double 0.0` to `d`
<3> declare `boolean b`;
assign `boolean true` to `b`
+
* Method call on a primitive type using the corresponding reference type.
+
[source,Painless]
----
<1> int i = 1;
<2> i.toString();
----
+
<1> declare `int i`;
assign `int 1` to `i`
<2> access `i` -> `int 1`;
box `int 1` -> `Integer 1 reference`;
call `toString` on `Integer 1 reference` -> `String '1'`
[[reference-types]]
==== Reference Types
A reference type is a named construct (object), potentially representing
multiple pieces of data (member fields) and logic to manipulate that data
(member methods), defined as part of the application programming interface
(API) for scripts.
A reference type instance is a single set of data for one reference type
object allocated to the heap. Use the
<<constructor-call, new instance operator>> to allocate a reference type
instance. Use a reference type instance to load from, store to, and manipulate
complex data.
A reference type value refers to a reference type instance, and multiple
reference type values may refer to the same reference type instance. A change to
a reference type instance will affect all reference type values referring to
that specific instance.
Declare a reference type <<painless-variables, variable>>, and assign it a
reference type value for evaluation during later operations. The default value
for a newly-declared reference type variable is `null`. A reference type value
is shallow-copied during an assignment or as an argument for a method/function
call. Assign `null` to a reference type variable to indicate the reference type
value refers to no reference type instance. The JVM will garbage collect a
reference type instance when it is no longer referred to by any reference type
values. Pass `null` as an argument to a method/function call to indicate the
argument refers to no reference type instance.
A reference type object defines zero-to-many of each of the following:
static member field::
A static member field is a named and typed piece of data. Each reference type
*object* contains one set of data representative of its static member fields.
Use the <<field-access, field access operator>> in correspondence with the
reference type object name to access a static member field for loading and
storing to a specific reference type *object*. No reference type instance
allocation is necessary to use a static member field.
non-static member field::
A non-static member field is a named and typed piece of data. Each reference
type *instance* contains one set of data representative of its reference type
object's non-static member fields. Use the
<<field-access, field access operator>> for loading and storing to a non-static
member field of a specific reference type *instance*. An allocated reference
type instance is required to use a non-static member field.
static member method::
A static member method is a function called on a reference type *object*. Use
the <<method-access, method call operator>> in correspondence with the reference
type object name to call a static member method. No reference type instance
allocation is necessary to use a static member method.
non-static member method::
A non-static member method is a function called on a reference type *instance*.
A non-static member method called on a reference type instance can load from and
store to non-static member fields of that specific reference type instance. Use
the <<method-access, method call operator>> in correspondence with a specific
reference type instance to call a non-static member method. An allocated
reference type instance is required to use a non-static member method.
constructor::
A constructor is a special type of function used to allocate a reference type
*instance* defined by a specific reference type *object*. Use the
<<constructor-call, new instance operator>> to allocate a reference type
instance.
A reference type object follows a basic inheritance model. Consider types A and
B. Type A is considered to be a parent of B, and B a child of A, if B inherits
(is able to access as its own) all of A's non-static members. Type B is
considered a descendant of A if there exists a recursive parent-child
relationship from B to A with none to many types in between. In this case, B
inherits all of A's non-static members along with all of the non-static members
of the types in between. Type B is also considered to be a type A in both
relationships.
*Examples*
* Reference types evaluated in several different operations.
+
[source,Painless]
----
<1> List l = new ArrayList();
<2> l.add(1);
<3> int i = l.get(0) + 2;
----
+
<1> declare `List l`;
allocate `ArrayList` instance -> `ArrayList reference`;
implicit cast `ArrayList reference` to `List reference` -> `List reference`;
assign `List reference` to `l`
<2> access `l` -> `List reference`;
implicit cast `int 1` to `def` -> `def`
call `add` on `List reference` with arguments (`def`)
<3> declare `int i`;
access `l` -> `List reference`;
call `get` on `List reference` with arguments (`int 0`) -> `def`;
implicit cast `def` to `int 1` -> `int 1`;
add `int 1` and `int 2` -> `int 3`;
assign `int 3` to `i`
+
* Sharing a reference type instance.
+
[source,Painless]
----
<1> List l0 = new ArrayList();
<2> List l1 = l0;
<3> l0.add(1);
<4> l1.add(2);
<5> int i = l1.get(0) + l0.get(1);
----
+
<1> declare `List l0`;
allocate `ArrayList` instance -> `ArrayList reference`;
implicit cast `ArrayList reference` to `List reference` -> `List reference`;
assign `List reference` to `l0`
<2> declare `List l1`;
access `l0` -> `List reference`;
assign `List reference` to `l1`
(note `l0` and `l1` refer to the same instance known as a shallow-copy)
<3> access `l0` -> `List reference`;
implicit cast `int 1` to `def` -> `def`
call `add` on `List reference` with arguments (`def`)
<4> access `l1` -> `List reference`;
implicit cast `int 2` to `def` -> `def`
call `add` on `List reference` with arguments (`def`)
<5> declare `int i`;
access `l0` -> `List reference`;
call `get` on `List reference` with arguments (`int 0`) -> `def @0`;
implicit cast `def @0` to `int 1` -> `int 1`;
access `l1` -> `List reference`;
call `get` on `List reference` with arguments (`int 1`) -> `def @1`;
implicit cast `def @1` to `int 2` -> `int 2`;
add `int 1` and `int 2` -> `int 3`;
assign `int 3` to `i`;
+
* Using the static members of a reference type.
+
[source,Painless]
----
<1> int i = Integer.MAX_VALUE;
<2> long l = Long.parseLong("123L");
----
+
<1> declare `int i`;
access `MAX_VALUE` on `Integer` -> `int 2147483647`;
assign `int 2147483647` to `i`
<2> declare `long l`;
call `parseLong` on `Long` with arguments (`long 123`) -> `long 123`;
assign `long 123` to `l`
[[dynamic-types]]
==== Dynamic Types
A dynamic type value can represent the value of any primitive type or
reference type using a single type name `def`. A `def` type value mimics
the behavior of whatever value it represents at run-time and will always
represent the child-most descendant type value of any type value when evaluated
during operations.
Declare a `def` type <<painless-variables, variable>>, and assign it
any type of value for evaluation during later operations. The default value
for a newly-declared `def` type variable is `null`. A `def` type variable or
method/function parameter can change the type it represents during the
compilation and evaluation of a script.
Using the `def` type can have a slight impact on performance. Use only primitive
types and reference types directly when performance is critical.
*Errors*
* If a `def` type value represents an inappropriate type for evaluation of an
operation at run-time.
*Examples*
* General uses of the `def` type.
+
[source,Painless]
----
<1> def dp = 1;
<2> def dr = new ArrayList();
<3> dr = dp;
----
+
<1> declare `def dp`;
implicit cast `int 1` to `def` -> `def`;
assign `def` to `dp`
<2> declare `def dr`;
allocate `ArrayList` instance -> `ArrayList reference`;
implicit cast `ArrayList reference` to `def` -> `def`;
assign `def` to `dr`
<3> access `dp` -> `def`;
assign `def` to `dr`;
(note the switch in the type `dr` represents from `ArrayList` to `int`)
+
* A `def` type value representing the child-most descendant of a value.
+
[source,Painless]
----
<1> Object l = new ArrayList();
<2> def d = l;
<3> d.ensureCapacity(10);
----
+
<1> declare `Object l`;
allocate `ArrayList` instance -> `ArrayList reference`;
implicit cast `ArrayList reference` to `Object reference`
-> `Object reference`;
assign `Object reference` to `l`
<2> declare `def d`;
access `l` -> `Object reference`;
implicit cast `Object reference` to `def` -> `def`;
assign `def` to `d`;
<3> access `d` -> `def`;
implicit cast `def` to `ArrayList reference` -> `ArrayList reference`;
call `ensureCapacity` on `ArrayList reference` with arguments (`int 10`);
(note `def` was implicit cast to `ArrayList reference`
since ArrayList` is the child-most descendant type value that the
`def` type value represents)
[[string-type]]
==== String Type
The `String` type is a specialized reference type that does not require
explicit allocation. Use a <<strings, string literal>> to directly evaluate a
`String` type value. While not required, the
<<constructor-call, new instance operator>> can allocate `String` type
instances.
*Examples*
* General use of the `String` type.
+
[source,Painless]
----
<1> String r = "some text";
<2> String s = 'some text';
<3> String t = new String("some text");
<4> String u;
----
+
<1> declare `String r`;
assign `String "some text"` to `r`
<2> declare `String s`;
assign `String 'some text'` to `s`
<3> declare `String t`;
allocate `String` instance with arguments (`String "some text"`)
-> `String "some text"`;
assign `String "some text"` to `t`
<4> declare `String u`;
assign default `null` to `u`
[[void-type]]
==== void Type
The `void` type represents the concept of a lack of type. Use the `void` type to
indicate a function returns no value.
*Examples*
* Use of the `void` type in a function.
+
[source,Painless]
----
void addToList(List l, def d) {
l.add(d);
}
----
[[array-type]]
==== Array Type
An array type is a specialized reference type where an array type instance
represents a series of values allocated to the heap. All values in an array
type instance are of the same type. Each value is assigned an index from within
the range `[0, length)` where length is the total number of values allocated for
the array type instance.
Use the <<new-array, new array operator>> or the
<<array-initialization, array initialization operator>> to allocate an array
type instance. Declare an array type <<painless-variables, variable>>, and
assign it an array type value for evaluation during later operations. The
default value for a newly-declared array type variable is `null`. An array type
value is shallow-copied during an assignment or as an argument for a
method/function call. Assign `null` to an array type variable to indicate the
array type value refers to no array type instance. The JVM will garbage collect
an array type instance when it is no longer referred to by any array type
values. Pass `null` as an argument to a method/function call to indicate the
argument refers to no array type instance.
Use the <<array-length, array length operator>> to retrieve the length of an
array type value as an int type value. Use the
<<array-access, array access operator>> to load from and store to individual
values within an array type value.
When an array type instance is allocated with multiple dimensions using the
range `[2, d]` where `d >= 2`, each dimension in the range `[1, d-1]` is also
an array type. The array type of each dimension, `n`, is an array type with the
number of dimensions equal to `d-n`. For example, consider `int[][][]` with 3
dimensions. The 3rd dimension, `d-3`, is the primitive type `int`. The 2nd
dimension, `d-2`, is the array type `int[]`. And the 1st dimension, `d-1` is
the array type `int[][]`.
*Examples*
* General use of single-dimensional arrays.
+
[source,Painless]
----
<1> int[] x;
<2> float[] y = new float[10];
<3> def z = new float[5];
<4> y[9] = 1.0F;
<5> z[0] = y[9];
----
+
<1> declare `int[] x`;
assign default `null` to `x`
<2> declare `float[] y`;
allocate `1-d float array` instance with `length [10]`
-> `1-d float array reference`;
assign `1-d float array reference` to `y`
<3> declare `def z`;
allocate `1-d float array` instance with `length [5]`
-> `1-d float array reference`;
implicit cast `1-d float array reference` to `def` -> `def`;
assign `def` to `z`
<4> access `y` -> `1-d float array reference`;
assign `float 1.0` to `index [9]` of `1-d float array reference`
<5> access `y` -> `1-d float array reference @0`;
access `index [9]` of `1-d float array reference @0` -> `float 1.0`;
access `z` -> `def`;
implicit cast `def` to `1-d float array reference @1`
-> `1-d float array reference @1`;
assign `float 1.0` to `index [0]` of `1-d float array reference @1`
+
* Use of a multi-dimensional array.
+
[source,Painless]
----
<1> int[][][] ia3 = new int[2][3][4];
<2> ia3[1][2][3] = 99;
<3> int i = ia3[1][2][3];
----
+
<1> declare `int[][][] ia`;
allocate `3-d int array` instance with length `[2, 3, 4]`
-> `3-d int array reference`;
assign `3-d int array reference` to `ia3`
<2> access `ia3` -> `3-d int array reference`;
assign `int 99` to `index [1, 2, 3]` of `3-d int array reference`
<3> declare `int i`;
access `ia3` -> `3-d int array reference`;
access `index [1, 2, 3]` of `3-d int array reference` -> `int 99`;
assign `int 99` to `i`