Photo by Ludde Lorentz on UnsplashPhoto by Ludde Lorentz on Unsplash

Variables, data types, static and non-static methods

Welcome to part 5 of the workshop. As usual, to keep each article as compact as possible, I will shortcut the queries to snippets. If you want to see the complete code, please consult my GitHub page for this workshop.

In this workshop, I will lead you through the basics of the language. We will have a look at some of the topics described in Painless Language Specification. We learn the different variables and types and how to declare them, how to cast to different data types, and we will define and operate with array lists and hashmaps. In short: there’s a lot to do, so let’s jump right in!


Primitive data types and their reference objects

You probably know the primitive data types like byte, short, char, int, long, float, double and boolean. Declaring them is pretty straightforward, here is an example with a long and a boolean:

boolean a;
long i = doc.market_cap.value;
if (i > 1000){
   a = true
   }else{
   a = false 
}

Primitives are declared in lowercase letters. With “def” the variables can be declared dynamically. This can be handy when the data type is unknown:

def a;
def b = doc.market_cap.value;
if (b > 1000){
  a = true
}else{
  a = false
}

Primitives don’t know methods, this is the big difference to reference objects like HashMapps, ArrayLists, etc. Primitives can be converted to reference objects, this is called boxing. The reference object for a primitive type has the same name, but with a capital first letter. For instance:

primitive       reference object
int          -> Integer
long         -> Long
char         -> Character
boolean      -> Boolean

So why is this important, you may ask? Shared API for package java.lang has 2 different types of methods that are provided by the reference objects: “static member methods” and “non-static member methods”. The methods are called different because they are handling boxing differently.

non-static methods

If you jump to the long section of the page for java.lang you can see at the end of the section methods like:

  • int compareTo(Long)
  • double doubleValue()
  • boolean equals(Object)
  • null toString()

This syntax is different from static methods because non-static methods need the instance that is created when the primitive data type was declared. When the method is called, the reference object is created internally. The syntax for a call is like this:

[primitive-type] [method]()

The primitive type is what will be returned from the method. Then there is the method and what data type the method expects as arguments. Let me show you some examples:

// double doubleValue()
double i = (double)doc.market_cap.value;
double a = i.doubleValue();

// boolean equals(Object)
boolean i = true; 
boolean j = false;
boolean a = i.equals(j);

// null toString()
long i = doc.market_cap.value;
String a = i.toString();

// int compareTo(Long)
long i = doc.market_cap.value;
long j = 2000;
int a = i.compareTo(j);

static methods

If we have a look again on Shared API for package java.lang, we can see methods that have the prefix static:

  • static long max(long, long)
  • static long divideUnsigned(long, long)
  • static int compare(long, long)
  • static int numberOfTrailingZeros(long)

Static methods need to be called by the reference type object, for instance, Integer, Long, Boolean, etc. The reference object will be used when the primitive is used as an argument for the static method. The reference object is the title of the section. In this case “Long”. Here are a few examples:

// static long max(long, long)
long i = doc.market_cap.value;
long j = 4000;
long a = Long.max(i,j);

// static long divideUnsigned(long, long)
long i = doc.market_cap.value;
long j = 4000;
long a = Long.divideUnsigned(i,j);

// static int compare(long, long)
Long i;
i = doc.market_cap.value;
long j = 4000;
long a = Long.compare(i,j);

// static int numberOfTrailingZeros(long)
Long i;
i = doc.market_cap.value;
long a = Long.numberOfTrailingZeros(i);

Arrays

Reference type objects are objects that are containing multiple fields of data and methods to manipulate them. Examples for reference data are Arrays, ArrayLists, and HashMaps. Unlike with primitive types, we need the “new” operator to define them.

The standard Arrays in painless are pretty old-school. The size of the Array must be defined when it is initialized, and there are no methods to extend it. Here is an example of a 1-d (one dimensional) and a 2-d Array:

int[] a = new int[] {1, 2, 3};
a[2] = 5;

int[][] i = new int[2][5];
i[0][0] = 12;

If you want to extend the Array, you need to create a new Array with the correct size and copy the fields of the original Array to the new. This is sone by a loop:

int[] a = new int[] {1, 2, 3};
int[] b = new int[4];
int c = a.length;
int i = 0;
while (i < c-1){
  i++;
  b[i] = a[i];
}
b[c] = 5;
emit(b[-1])

ArrayLists

Here is an example with an ArrayList. Please note, the size of the Array is not defined at the initialization:

long mc = doc.market_cap.value;
List al = new ArrayList();
al.add(mc);
al.add(10);
emit(al.get(0) + al.get(1))

HashMaps

A HashMap stores key/value pairs like dictionaries in Python or Hashes in Perl. The size will not be defined and like with the other reference type objects, we use the new operator to initialize them.

Map mp = new HashMap();
mp.put('one', 1);
mp.put('two', 2);
emit(mp.get('two'))

String data type

Strings are a reference data type. You declare them with string literals, there is no need for the new operator to declare them. You can concatenate strings with the ‘+’ operator:

"type": "keyword",
"script": {
  "source": """
  String mc = doc['market_cap_string.keyword'].value;
  String ticker = doc['ticker_symbol.keyword'].value;
  String phrase = " is worth ";
  emit(ticker + phrase + mc)
  """

Strings provide many useful methods, please visit the StringBuilder and StringBuffer Section at Shared API for package java.lang

// int length()
// null substring(int)
String mc = doc['market_cap_string.keyword'].value;
int len = mc.length();
def mc_clean = mc.substring(0, len - 1);
emit(mc_clean)

Casting

With casting a data type can be converted. For example, a variable of type long should be converted to a type float with double precision:

long mc = doc.market_cap.value;
double mc_double = (double)mc;

Conclusion

If you made it here: Congratulations! You should now be able to declare the basic variables and data types. And more important: you should be able to use the official documentation of the API and call the static and non-static methods for each data type.

If Google brought you here, you might check also the start of the series, or the whole series.

And as always: the full queries and code is available on the GitHub page for this workshop

Schreibe einen Kommentar