II Variables

12. Introduction

Variables are (perhaps unsurprisingly), items that can change. Essentially a variable is a “box” that can hold a value. Groovy is a “dynamic language” in that it allows you to easily store and manipulate variables regardless of their value. This places it in similar company to Python and Ruby but, as a child of Java, Groovy can also operate as a “typed language”. In typed languages we can specify the data type (e.g. a number or piece of text) of the variable.

Groovy lets us work in both language modes - dynamic and typed - and this flexibility makes it that much easier to use.

13. Declaring Variables

Groovy provides a few ways to create a variable but the best one to start with is to use the def keyword. In the example below I define (def) a new variable named score that can be used to hold a value later in my program:

Defining a variable
def score

In the next example I assign score a value of 10 and ask Groovy to display the value of score using println:

Using a variable
def score
score = 10
println score

Instead of declaring score and then assigning it the value 10 I can do this on a single line using def score = 10. I do just this in the example below and then change the value of score (it is a variable after all) - try this in your Groovy Console and the printlns will show you the value of score after it’s been set.

Changing the value
def score = 10
println score
score = 11
println score

You’ll note that the second time I use score I don’t need the def prefix as I’ve already declared score and don’t need to redeclare it.

If we’re declaring a number of variables we could provide a def on each line:

def myNumber
def myName

Alternatively, the previous example could be represented on a single line in which each variable is separated by a comma (,):

def myNumber, myName

You can assign values to variables defined on a single line:

def number1 = 10, number2 = 20

A set of variables can be assigned values from a list (multiple assignment):

Multiple assignment
def number1, number2
(number1, number2) = [10, 20]

assert number1 == 10
assert number2 == 20

In the next example a third variable is introduced but the assignment list only provides two elements. This will result in number1 and number2 being set but number3 remaining without a value (null):

Multiple assignment
def number1, number2, number3
(number1, number2, number3) = [10, 20]

assert number1 == 10
assert number2 == 20
assert number3 == null

Finally, we can perform multiple assignment at the point of declaring the variables:

Multiple assignment
def (number1, number2, number3) = [10, 20, 30]

assert number1 == 10
assert number2 == 20
assert number3 == 30

Variable names

Variable names must meet the following criteria:

  • Must start with a letter (upper-case [A-Z] or lower-case [a-z]) - The underscore (_) is also allowed but this is very strongly discouraged
  • Must only contain letters, digits (0-9) or an underscore (_)
    • The dollar-sign ($) is also allowed but very strongly discouraged
  • Must not match a keyword (reserved word)

The use of literate variable names that comply to the criteria is encouraged. For example, a variable named x provides little information as to its role whereas accountNumber is likely to be clear within the context of a broader system.

Data Types

Data types define the sort of data a variable can hold. Most programming language feature the following data types:

  • Booleans
    • A logical value of true or false
  • Characters and strings
    • A character is a single letter, number or symbol (e.g. #)
    • A piece of text is referred to as a “string”
  • Numbers
    • Integers (whole numbers) both positive and negative
    • Decimals (fractional numbers) both positive and negative
  • Dates and times
    • You know, like dates and times
  • Lists and sets
    • A variable that holds a number of values (list)
    • A variable that holds unique values (set)
  • Maps
    • A variable that holds a number of values, each referred to by a key
  • Ranges
    • A numeric sequence between a start and an end value - e.g. 1 to 10

Being an object-oriented programming language, Groovy lets you also define your own types of objects (called classes).

Groovy allows you to create and use variables without declaring a data type - often called dynamic typing. Java, on the other hand, uses static typing and you need to tell Java the data type you want to use when declaring a variable. Once again, Groovy is flexible and lets you use dynamic or static typing (or both) in your programs.

14. Objects

But what is an object? Well, an object is an encapsulation of properties and methods:

  • Properties and Fields are variables that hold data about the object
    • For example, a person object may have properties such as name and email
    • There is a difference between Properties and Fields but we’ll look into that later.
  • Methods are a means for accessing and manipulating the object’s properties
    • For example a person object may have methods such as getName() and setName(name)
    • Methods can take parameters and/or return values. For example: getName() would return the person’s name; and setName(name) takes 1 parameter (name) and sets the person’s name to that value
    • Methods are sometimes called functions

We use the class structure to define this assembly of properties and methods.

Declaring and using a class

Let’s look at a Groovy script that declares a new class:

Declaring a new class
class Person {
    def name
    def email

    def getName() {
        return this.name
    }

    def setName(name) {
        this.name = name
    }

    def getEmail() {
        return this.email
    }

    def setEmail(email) {
        this.email = email
    }
}

// Create a new variable to hold an instance of the Person class
def david = new Person(name: 'David', email: 'david@example.com')

// Change David's email address:
david.setEmail('dave@example.com')

// Print out David's information
println david.getName()
println david.getEmail()

A class is defined using the class keyword and it’s best practice to use an uppercase letter for the first character: class Person {

We declare the two properties in much the same way as we do for any variable:

Properties
def name
def email

A number of methods are declared to let us set and retrieve (get) the values of the object’s properties:

Methods
def getName() {
    return this.name
}

def setName(name) {
    this.name = name
}

def getEmail() {
    return this.email
}

def setEmail(email) {
    this.email = email
}

After we’ve declared the Person class we can now create instances of the class and assign values to the properties:

Creating an instance
def david = new Person(name: 'David', email: 'david@example.com')

We use def david as we would for other variables and then use new Person to indicated that david will hold an instance of the Person class. Lastly we call a special method called a constructor that Groovy provides us for our objects: (name: 'David', email: 'david@example.com'). This sets up david with starting values for the properties.

At some point David changes his email address so we call the setEmail method:

david.setEmail('dave@example.com')

You can see that the method call uses dot-point notation of <variable name>.<method name> - the dot (.) separates the variable name (david) from the method (setEmail).

Lastly, we use the two get methods to display david’s information:

Calling methods
println david.getName()
println david.getEmail()

The example Person class has demonstrated a number of Groovy’s object-oriented programming syntax:

  1. Creating a new class with properties and methods
  2. Creating a new instance of the class and calling its constructor
  3. Changing (setting) and retrieving (getting) the instance’s properties

You can create lots of Person instances and each will exist in their own context. This means that david and sarah don’t get mixed up:

Creating instances
def david = new Person(name: 'David', email: 'david@example.com')
def sarah = new Person(name: 'Sarah', email: 'sarah@example.com')

Useful Methods

In the Groovy/Java family tree, java.lang.Object is the grand-daddy of all classes. Using a system called “inheritance”, each new class inherits attributes such as methods and properties from their forebears. Even the Person class I described above inherits from java.lang.Object and the Groovy developers have enhanced that class further! This means that all classes have built-in features that we can access. Let’s look at a few of them.

class

The class property is used to access the Class that defines the object. This can be really useful when we want to check what sort of object we’re dealing with.

The class property
class Person {
    def name
    def email
}

def david = new Person(name: 'David', email: 'david@example.com')

println david.class.name

dump()

This will return a String that describes the object instance’s internals. Try out the following code to see what gets dumped:

The dump method
class Person {
    def name
    def email
}

def david = new Person(name: 'David', email: 'david@example.com')

println david.dump()

with()

This method works with closures (we’ll cover them later) to give us an easy format for accessing a object’s properties in methods. In the example below I wrap some code using with and don’t have to use david.name and david.email to access those properties:

The with method
class Person {
    def name
    def email
}

def david = new Person(name: 'David', email: 'david@example.com')

david.with {
    println name
    println email
}

Existing classes

The great strength/benefit/bonus of an object-oriented programming platform such as Java is the vast array of existing libraries of objects that you can reuse in your code. In Groovy and Java the listing of these available objects are referred to as the Application Programming Interface (API).

If we were going to create a variable to hold a string (a piece of text) we would do something like:

Creating a new String
def quote = 'Well may we say "God save the Queen", because nothing will save the\
 Governor-General!'

We could also use the following code to do exactly the same thing as the code above:

Also creating a new String
def quote = new String('Well may we say "God save the Queen", because nothing wi\
ll save the Governor-General!')

This looks similar to the way we created an instance of the Person class - we create a new instance of String and pass the text into the constructor.

Now that we have our quote string we actually also get a number of methods that help us handle our variable:

Handy String methods
def quote = 'Well may we say "God save the Queen", because nothing will save the\
 Governor-General!'

//Display the quote in upper case letters
println quote.toUpperCase()

//Display the quote backwards
println quote.reverse()

//Display the number of characters in the quote
println quote.size()

The example above demonstrates how we can call methods on an object instance and you’ll see this used in the rest of the tutorials. Be sure to try out the code above to see what it does!

Classes and literal values

Literal values are best thought of the value you would write down:

  • Boolean:
    • true
    • false
  • Numbers:
    • 42
    • 3.14
  • Strings (text):
    • ‘hi there’

We can call methods directly on literal values as Groovy will create an appropriate object instance for us:

Calling a method from a literal
assert 1.plus(1) == 2

This definitely looks a bit odd but think of it this way:

  1. Groovy sees the literal value 1 followed by a method call
  2. Groovy creates a number object instance for 1
  3. Groovy then calls the plus method against the new number instance

This can start to be very useful when you look at lists and ranges - something we’ll get to soon.

Lastly, as the literal is put into an object we can access methods and properties for the object. In the example below I can see what data type Groovy is actually using when I use 3.14:

Accessing properties from a literal
println 3.14.class.name

15. Booleans

Boolean variables are perhaps the simplest and can hold a value of either true or false.

Booleans
def truth = true
def lies = false

Useful Methods

Booleans have a small number of methods that you generally won’t find yourself using as they (mostly) have equivalent operators that are more “natural” to read and write.

The and(right) method performs a logical ‘and’

The and method
def truth = true
def lies = false
assert truth.and(lies) == false

The conditional And operator (&&) is equivalent to the and method and the assertion above could also be written as assert truth && lies == false

The or(right) method performs a logical ‘or’

The or method
def truth = true
def lies = false
assert truth.or(lies) == true

The conditional Or operator (||) is equivalent to the or method and the assertion above could also be written as assert truth || lies == true

16. Numbers

There are two main types of numbers you’re likely to need:

  • Integers (whole numbers)
  • Decimals

Groovy also gives us scientific notation and other number systems and we’ll take a look at how you use them.

Integers

Integers are whole numbers and can be negative or positive:

Using Integers
def age = 27
def coldDay = -8

Groovy will also handle very large numbers:

Large numbers
// 1 astronomical unit (au)
def distanceEarthToSun = 149597870700
def distanceNeptuneToSun = distanceEarthToSun * 30

Decimals

Decimal numbers provide a fraction and can be negative or positive:

Using decimals
def pi = 3.14159

// Measured in celsius
def absoluteZero = -273.15

Scientific notation

Base-10 (decimal) scientific notation (a * 10^b) can also be used by placing an e or E before the exponent:

Using SN
def atomicMass = 1.67e-27

The next example sets the au variable to 1.49597870700 * 10^{11} and then checks to make sure I haven’t messed up the exponent:

Just a check
def au = 1.49597870700e11
assert au == 149597870700

In the previous two examples you can see a signed (positive or negative) integer as the exponent:

  • e-27 is negatively signed
  • e11 can also be written as e+11 and is positively signed

Number Systems

Most of the time we deal with decimal (base-10) numbers but there are other number systems out there. If we want to use the number 15 in base-10 we just type 15 but we can also use:

  • Binary (base-2) by prefixing 0b
    • That’s a zero followed by lower-case “b”
  • Octal (base-8) by prefixing 0
    • That’s just zero
  • Hexadecimal (base-16) by prefixing 0x
    • That’s a zero followed by lower-case “x”

The code below illustrates the many faces of the number 15 (base-10):

Different number systems
println 0b1111    //Binary
println 15         //Decimal
println 017       //Octal
println 0xf       //Hexadecimal

To help you deal with long numbers Groovy lets you use underscores (_) to visually break up the number without changing its value:

Formatting large numbers
assert 1_000_000 == 1000000
assert 0b0001_0110_1101 == 365

Let’s close with a joke:

Lolz
def value = 0b10

println "There are only $value types of people in the world - those who know bin\
ary and those who don't"

Useful Methods and Properties

Groovy (Java) numbers trace their lineage (inherit) back to java.lang.Number. The Number class provides methods to covert between different types of numbers (integer, decimal etc) - we’ll cover this in the chapter on Data Types.

Most numerical classes (e.g. Integer) provide the handy max and min methods that let you compare two numbers of the same numerical type:

max and min
assert Integer.max(10, 2) == 10
assert Integer.min(10, 2) == 2

17. Strings

There are two main ways in which you can declare a string in Groovy: single or double quotes

The String section
Method Usage
Single quotes ('...') These are fixed strings and tell Groovy that the string is as we’ve written it (e.g. def pet = 'dog').
Double quotes ("...") These are called GStrings and let us interpolate (insert) variables into our string. (e.g. def petDescription = "My pet is a $pet")
Three single quotes ('''...''') A multi-line fixed string
Three double quotes ("""...""") A multi-line GString

Here’s a quick example of a fixed string and a GString in action:

Fixed strings and GStrings
def pet = 'dog'
def petDescription = "My pet is a $pet"
println petDescription

Escape sequences

Strings can contain escape sequences, allowing you to use non-printable characters in your text.

Escape sequences
Sequence Character
\n line feed
\f form feed
\r carriage return
\t horizontal tab
\’ single quote
\” double quote
\\ backslash

The line feed (\n) is often used to move to a new line:

The line feed
print 'Hi \n there\n'

You’ll notice the use of print in the example above - the final \n performs the same as println and moves to a new line.

The form feed (\f) and carriage return (\r) aren’t often used. Form feed indicates a new page and carriage return goes back to the start of the line.

The horizontal tab (\t) is essentially the same as the tab key on your keyboard. It’s useful for formatting things like tables of information:

Sequences
println 'name\tage\tcolour'
println 'Sam\t12\tblue'
println 'Alice\t8\tgreen'

If you wish to use a quote within your string that matches the quote type you’re using to surround your string then you need to escape the internal quote using the \ character. In the code below you can see the quotes being escaped (\' and \"):

Escape, escape!
println 'That\'s mine'
println "I said \"NO!\""

As the backslash (\) is used to escape characters, it needs an escape of its own. In order to use a backslash in a string you need to double it up (\\) as in the example below:

Backslashing
println 'c:\\documents\\report.doc'

GStrings

In order to have Groovy interpolate the value of a variable we use the $ symbol in front of the variable name - as you can see with $pet below:

GStrings
def pet = 'dog'
println "I own a $pet"

This can be handy if you have a number of variables that you’d like to use in a string:

Interpolating strings
def name = 'Sally'
def hobby = 'surfing'
println "Did you know that $name likes $hobby?"

GStrings also let you interpolate more complicated expressions into a string by using ${...}. In the following example we perform a calculation within the GString:

Operation in a GString
println "10 to the power of 6 is ${10**6}"

We can also access information about a variable in the same manner:

Operation in a GString
def word = 'Supercalifragilisticexpialidocious'
println "$word has ${word.length()} letters"

Multiline Strings

The examples given so far use short strings but longer strings would be cumbersome to type using \n all over the place. Instead, Groovy provides multiline strings - the code below declares a multiline fixed string:

A Multiline string
def poem = '''But the man from Snowy River let the pony have his head,
And he swung his stockwhip round and gave a cheer,
And he raced him down the mountain like a torrent down its bed,
While the others stood and watched in very fear.'''

print poem

If you run the code above you’ll see that new lines are used at the correct points in the display but the first line is not quite right. You can modify this slightly and place a backslash (\) at the start of the string - using statement continuation for readability:

Fixing the first line
def poem = '''\
But the man from Snowy River let the pony have his head,
And he swung his stockwhip round and gave a cheer,
And he raced him down the mountain like a torrent down its bed,
While the others stood and watched in very fear.'''

print poem

GStrings can also be defined using the multiline format:

A multiline GString
def animal = 'velociraptor'

println """But the man from Snowy River let the ${animal} have his head,
And he swung his stockwhip round and gave a cheer,
And he raced him down the mountain like a torrent down its bed,
While the others stood and watched in very fear."""

Building Strings

Working with basic strings is fine but if you need to build up a large piece of text throughout a program they can become very inefficient. We’ll look into this in the tutorial on Operators.

Useful Methods

Strings (text) are important aspects to human-based systems so most programming languages provide a number of methods for modifying, search, slicing and dicing strings. Groovy provides a number of helpful methods you can use with strings and we’ll look at just a few of them here:

  • length() : returns the number of characters in a string
  • reverse(): returns the mirrored version of the string
  • toUpperCase() and toLowerCase(): returns the string with all of the characters converted to upper or lower case.
Some String methods
def str = 'Hello, World'
println str.length()
println str.reverse()
println str.toUpperCase()
println str.toLowerCase()

The trim() method returns the string with any leading and trailing whitespace removed:

Trimming a String
def str = '  Hello, World  '
println str.trim()

The substring method returns a subsection of a string and can be used in two possible ways:

  • Provide a start index (e.g. substring(7)) to get the subsection that includes that index (i.e. the 7th character in the string) through to the end of the string
  • Provide a start and an end index (e.g. substring(7, 9)) to get the subsection that includes that start index through to the end index of the string
Substrings
def str = 'Hello, World'
println str.substring(7)
println str.substring(7,9)

A number of methods are provided to help you with basic searching:

  • The indexOf and lastIndexOf methods return the index (location) of the specified character in the string
  • contains, startsWith, and endsWith return true or false if the supplied parameter is located within the string
Basic searching
def str = 'Hello, World'

//These methods return the index of the requested character
println str.indexOf(',')
println str.lastIndexOf('o')

//These methods check if the string contains another string
println str.contains('World')
println str.startsWith('He')
println str.endsWith('rld')

The replace method lets us provide a string that we want to change to a new value:

Replacing text
def str = 'Hello, World'

println str.replace('World', 'Fred')

Lastly, and a favourite of mine, is toURL(). This converts a String to a URL object which, in Groovy has a great text property that lets us load the text of our favourite web page:

println 'http://www.example.com/'.toURL().text

18. Collections

Collections group a number of values in a single container. The Java Collections Framework provides a really extensible and unified approach to handling collections. Groovy makes these even easier to use and focusses on two key collection types:

  • Lists: provide a container for several values
  • Maps: use keys as a method for indexing a set of values

Lists

List variables contain several items and are declared using square brackets ([...]).

The example below declares a variable (temperatures) as an empty list:

Declaring an empty list
def temperatures = []

The next examples declares the temperatures list with some initial values:

Declaring a list with values
def temperatures = [10, 5, 8, 3, 6]

In the temperatures example the list contains just numbers but Groovy lists can contain a mix of data types:

Lists can contain mixed types
def mixed = [1, true, 'rabbit', 3.14]
println mixed[2]
println mixed[-3]
println mixed.get(3)

The square brackets [] are used to create a list but are also used to refer to indexes in the list (e.g. mixed[2]) - this is often referred to as subscript notation. In the example above you’ll notice I’ve printed out mixed[2] - the list item with index (subscript) 2. Somewhat confusingly this causes rabbit to be displayed. This is because lists are zero-based and the first item is at index 0, not index 1. Where we use mixed[2] we’re asking for the third item in the list.

It may surprise some programmers that println mixed[-3] is valid - it’s a very handy approach to accessing list items from the end of the list. Item -1 is the last in the list so mixed[-3] will be the value true.

The get() method can also be used to access a list element by its index - e.g. mixed.get(3) gives us 3.14.

I can provide multiple indexes in the subscript notation and grab the specified elements from the list. In the example below I grab elements 0 and 2 (temperatures[0, 2]) and then elements 1, 3 and 4 (temperatures[1, 3, 4]):

Using indexes with lists
def temperatures = [10, 5, 8, 3, 6]
assert temperatures[0, 2] == [10, 8]
assert temperatures[1, 3, 4] == [5, 3, 6]

Ranges can also be used in the subscript notation and, as demonstrated in the example below, return a list containing the items whose indexes are included in the range:

Using ranges with lists
def temperatures = [10, 5, 8, 3, 6]
assert temperatures[1..3] == [5, 8, 3]

We can also use a mix of individual indexes and ranges as we see fit:

Indexes and ranges with lists
def temperatures = [10, 5, 8, 3, 6]
assert temperatures[0..1, 3] == [10, 5, 3]
assert temperatures[0..1, 1..3] == [10, 5, 5, 8, 3]
assert temperatures[0..1, 1..3, 4] == [10, 5, 5, 8, 3, 6]

What? Let’s take a look:

  • temperatures[0..1, 3] returns a list containing the elements of temperatures with the indexes 0, 1 and 3
  • temperatures[0..1, 1..3] returns a list using two ranges to select the indexes. As index item 1 is requested twice, the returned list features the item (5) twice.
  • temperatures[0..1, 1..3, 4] does the same as the previous statement but adds in the item at index 4

Adding elements

To add an element to a list we use the add() method or the << operator:

Adding elements
def mixed = [1, true, 'rabbit', 3.14]
mixed << 'biscuit'
mixed.add(101)
println mixed

Sets

Sets are much like lists but each element in a set is unique:

Declaring a Set
def names = ['sally', 'bob', 'sally', 'jane'] as Set
println names

If you try the code above you’ll get [sally, bob, jane] - the set just ignores the repeated element.

Useful List Methods

The size() method returns the number of elements in the list:

List size
def periodic = ['hydrogen', 'helium', 'lithium']
println periodic.size()

The first() and last() methods return the first and last elements in a list. The head() method is synonymous with first().

First and last methods
def periodic = ['hydrogen', 'helium', 'lithium']
println periodic.first()
println periodic.head()
println periodic.last()

The tail() method returns the list minus the first (head) element and the init() method returns the list minus the last element:

Tail method
def periodic = ['hydrogen', 'helium', 'lithium']
assert periodic.tail() == ['helium', 'lithium']
assert periodic.init() == ['hydrogen', 'helium']

The contains() method returns true if the requested element is contained in the list:

Contains method
def periodic = ['hydrogen', 'helium', 'lithium']
assert periodic.contains('helium') == true

The reverse() method returns the mirror of the list:

Reverse method
def periodic = ['hydrogen', 'helium', 'lithium']
println periodic.reverse()

The sort() will sort the elements in a “natural” order. Basically, this relies on the list elements being comparable in some manner. The sort method is best used when the list contents are all of the same type (e.g. strings or numbers):

Sort method
def periodic = ['hydrogen', 'helium', 'lithium']
periodic.sort()

The asImmutable() method is a handy way to set the list contents in stone - “Immutable” essentially means “unchanging”.

Don’t go changing
def friends = ['fred', 'sally', 'akbar'].asImmutable()

//This next line will cause an exception:
friends << 'jake'

Maps

Maps allow us to build up a type of look-up table using keys and values. Other languages call these dictionaries or associated arrays.

An empty map is declared using [:] and the example below shows this in use when declaring the periodic variable.

Declaring an empty map
def periodic = [:]

Each key in a map is unique and points to a value in the map. In the example below we see the start of a basic periodic table by declaring a variable (periodic) with a set of key-value pairs (key: value) each separated by a comma (,) and held within square brackets ([...]):

Declaring a map with elements
def periodic = ['h': 'hydrogen',
		'he': 'helium',
		'li': 'lithium']

println periodic['li']
println periodic.li
println periodic.get('li')

You should also note that we can access map items using:

  1. The key in square brackets ([])
    1. Much as we did with lists: println periodic['li'].
    2. This is often referred to as subscript notation.
  2. We can also use the period (.) followed by the key:
    1. As in println periodic.li.
    2. This is often referred to as dot-point notation
  3. Lastly, the get() method is passed a key and returns the associated value

The keys in a map can be names provided they adhere to the same rules we follow for variable names. That means that the keys in periodic don’t have to be written as strings:

Keys as names
def periodic = [h: 'hydrogen',
        he: 'helium',
        li: 'lithium']

Adding elements

To add an element to a map we can use the square bracket, dot-point notation, << operator, or put() method to add on a new key/value pair:

Adding elements
def periodic = ['h': 'hydrogen',
        'he': 'helium',
        'li': 'lithium']

periodic['be'] = 'Beryllium'
periodic.b = 'Boron'
periodic << ['c': 'Carbon']
periodic.put('n', 'Nitrogen')

println periodic

Keys

Map keys don’t have to be strings - they can be a mix of strings, numbers or other objects. Let’s look at an example then go through the various bits of code:

Different types of keys
class Chicken {
    def name

    String toString() {
        return "I am $name".toString()
    }
}

def cluckers = new Chicken(name: 'Cluckers')

def mixedMap = [
        12        : 'Eggs in a carton',
        'chicken' : 'Egg producer',
        (cluckers): 'Head chicken'
]

println mixedMap[12]
println mixedMap.get(12)

println mixedMap.chicken
println mixedMap['chicken']
println mixedMap.get('chicken')

println mixedMap[(cluckers)]
println mixedMap.get(cluckers)

println mixedMap

In the example above:

  1. I create a new class (Chicken)
    1. … and store a new instance of Chicken in the variable cluckers
  2. I then create a map variable called mixedMap with different types of keys:
    1. 12 is a number
    2. 'chicken' is a string
    3. (cluckers) indicates that the key is a variable value
  3. I use the square-bracket notation and get method to access the value aligned to the key 12
    1. mixedMap.12 won’t work
  4. I use the square-bracket, dot-point and get method to access the value aligned to the key 'chicken'
  5. I use the square-bracket notation and get method to access the value aligned to the key (cluckers)
    1. mixedMap.cluckers
  6. println mixedMap is called to display the map contents

For those interested in such things, the (cluckers) key isn’t affected if I change the value of cluckers later on. If you append the code below to the chicken example you’ll see that mixedMap.get(cluckers) will now return null as the match fails. You’ll also notice that println mixedMap is the same output you get before changing cluckers:

Changing objects used as keys
cluckers = new Chicken(name: 'Bill')
println mixedMap.get(cluckers)
println mixedMap

Useful Map Methods

As with lists, the size() methods returns the number of elements in a map.

The get method can be used to get the value for the requested key. A second optional parameter can be provided and is returned if the map does not contain the requested key:

Get method
def periodic = ['h': 'hydrogen',
        'he': 'helium',
        'li': 'lithium']

println periodic.get('he')
println periodic.get('he', 'Unknown element')
println periodic.get('x', 'Unknown element')

The keySet() method returns a list containing all of the keys in a map and values() returns a list of the values in a map:

keySet method
def periodic = ['h': 'hydrogen',
        'he': 'helium',
        'li': 'lithium']

println periodic.keySet()
println periodic.values()

The containsKey() and containsValue() methods are useful for checking on map contents:

Checking for keys and values
def periodic = ['h': 'hydrogen',
        'he': 'helium',
        'li': 'lithium']

println periodic.containsKey('li')
println periodic.containsValue('carbon')

The asImmutable() method works for maps in the same manner as it does for lists:

Don’t go changing
def periodic = ['h': 'hydrogen',
        'he': 'helium',
        'li': 'lithium'].asImmutable()

//This will cause an exception:
periodic.x = 'new element'

19. Arrays

For my money, the collections we’ve just looked at (lists, sets, maps) are more versatile than arrays and collections are my preferred approach. However, there’s a lot of code out there using arrays so let’s take a quick look.

Arrays contain a fixed number of elements of a specified data type. Let’s look at an example of array declaration and usage:

Declaring an array
Number[] point = new Number[2]

point[0] = 27
point[1] = -153

assert point.length == 2

So let’s dissect that chunk of code:

  • The point variable is declared using Number[] point = new Number[2]
    • Number[] indicates that we want an array of Numbers
    • [] indicates that the variable is an array, not just a single Number value
    • new Number[2] sets point to be an empty array that can contain two (2) elements of the Number class (or a subtype thereof).
    • Don’t use def as we’re specifying the data type
  • Arrays are zero-based, meaning that the first element is at index 0
    • point[0] is the first element
    • point[1] is the second
  • point.length returns the number of elements in the array
    • Note that the range of indexes for an array is 0..(point.length - 1)
    • point.size() would also work and provides the same result as point.length

If I’d tried something like point[2] = 99 I would get a java.lang.ArrayIndexOutOfBoundsException as the array can only hold 2 elements.

It’s important to note that the size of an array is fixed at declaration. If you decide that you need to expand the array then you’ll slap your forehead and ask “Why didn’t I use collections?”. If you dig your heels in and stay with arrays you might check out the java.lang.System.arraycopy method and learn the gentle art of copying and resizing arrays. Then, you’ll start using collections.

We can be more direct in creating the array and provide the values up-front. In the example below I create an array that can hold two elements and I load the values into the array:

Setting elements at creation
Number[] point = [27, -153]

So, why did I pick Number? Well, I want an array of numerical values but perhaps wasn’t sure which type of numbers. Provided the values I put into the array are subtypes of Number, all will be well. That means the following will work fine and nothing will be truncated:

Number[] point = [27.9, -153]

If I really wanted to be specific about the type of number I could have declared point as an array of Integer values:

Integer[] point = [27, -153]

Arrays can also be declared to be of a primitive type such as int1:

int[] point = [27, -153]

Going further with subtypes etc, arrays can be of any type and the Object class provides a flexible type when your array needs to hold a mixture of values (e.g. numbers, strings, various types):

A mixed bag array
Object[] bag = new Object[4]
bag[0] = true
bag[1] = 'Rabbit'
bag[2] = 3.14
bag[3] = null

Without wanting to be repetitive, the example above would probably be easier to work with if we used a collection such as a list.

Manipulating arrays

We’ve seen the size() method and length property - both indicating how many elements the array can hold.

Sorting an array is easy with the sort() method:

Sorting an array
Number[] nums = [99, 10.2, -7, 99.1]
nums.sort()
println nums

Of course sort() works well if the element types have a meaningful sorting order but try out the following code and you’ll see that the sort() perhaps isn’t overly useful on mixed values:

Can this be sorted?
Object[] bag = new Object[4]

bag[0] = true
bag[1] = 'Rabbit'
bag[2] = 3.14
bag[3] = null

println bag.sort()

Use the Arrays.asList() static method to get a copy of an array into a list (collection):

Arrays to lists with asList
Number[] nums = [99, 10.2, -7, 99.1]
def list = Arrays.asList(nums)

Alternatively, you can use the as operator to cast the array to a List.

Arrays to lists with as
Number[] nums = [99, 10.2, -7, 99.1]
def list = nums as List

Check out the java.util.Arrays class for more array methods.

  1. Primitive types are discussed in the Data Types chapter.

20. Ranges

Ranges define a starting point and an end point. Let’s look at a well-known type of range:

Declaring a range
def countdown = 10..0

println countdown.getFrom()
println countdown.getTo()

The countdown range starts at 10 and goes down to 0. The notation should be easy to decipher: <start>..<end>.

Printing out a range variable will display that a range is rather like a list of values - in the case of countdown they’re numbers:

Ready for launch
def countdown = 10..0
println countdown

Whilst my examples so far all go down, you can just as easily have a range that goes up:

Going up
def floors = 1..10
println floors

You can also use decimals but note that it is only the integer (whole-number) component that is stepped through:

Decimals in ranges
def countdown = 10.1..1.1
println countdown

Half-Open Ranges

Ranges aren’t just limited to inclusive ranges such as 1..10. You can also declare a half-open range using ..< - that’s two periods and a less-than. This denotes that the range ends prior to the number to the right. In the example below I setup a grading criteria that avoids an overlap between the grades:

Half-open range declarations
def gradeA = 90..100
def gradeB = 80..<90
def gradeC = 65..<80
def gradeD = 50..<65
def gradeF = 0..<50

I could tweak the above code if I want to get fancy:

A fancier approach
def gradeA = 90..100
def gradeB = 80..<gradeA.getFrom()
def gradeC = 65..<gradeB.getFrom()
def gradeD = 50..<gradeC.getFrom()
def gradeF = 0..<gradeD.getFrom()

Ranges of Objects

Ranges are primarily used with numbers but they can be of any object type that can be iterated through. This basically means that Groovy needs to know what object comes next in the range - these objects provide a next and previous method to determine this sequence. Over time you’ll discover various options for use in ranges but numbers really are the main type.

Apart from numbers, individual characters (letters) can be used in ranges. In the example below I create a range of lower-case letters:

A range of characters
def alphabet = 'a'..'z'
println alphabet

Ranges and Enums

Ranges can be handy when dealing with enums as they give us the ability to set a subset of enum values. In the example below I create a handy helpdesk tool:

  1. Setup an enum listing the possible ticket priorities
  2. Create a new class to describe helpdesk tickets
  3. Setup a helpdeskQueue containing a list of tickets
  4. Set the focus variable as a range of Priority values
  5. Go through the list of tickets and pick up any that are set to the priority I care about.
Using a helpdesk ticket enum
enum Priority {
    LOW,MEDIUM,HIGH,URGENT
}

class Ticket {
    def priority
    def title
}

def helpdeskQueue = [
    new Ticket(priority: Priority.HIGH, title: 'My laptop is on fire'),
    new Ticket(priority: Priority.LOW, title: 'Where is the any key'),
    new Ticket(priority: Priority.URGENT, title: 'I am the CEO and I need a coff\
ee'),
    new Ticket(priority: Priority.MEDIUM, title: 'I forgot my password')
]

def focus = Priority.HIGH..Priority.URGENT

for (ticket in helpdeskQueue) {
    if (ticket.priority in focus) {
        println "You need to see to: ${ticket.title}"
    }
}

Try the example above out with various settings for the focus variable:

  • def focus = Priority.MEDIUM..Priority.URGENT
    • Gives us more tickets to see to :(
  • def focus = Priority.HIGH..Priority.LOW
    • Is actually similar to 4..1 and leaves out the tickets marked URGENT

Ranges and List Indexes

You can access a subset of a list using a range subscript. In the example below I use the subscript [1..3] to grab a new list containing elements 1 through 3 of the temperatures list.

Accessing range elements
def temperatures = [10, 5, 8, 3, 6]
def subTemp = temperatures[1..3]
assert subTemp == [5, 8, 3]

Ranges and Loops

Ranges are most often see when we’re using loops - we’ll get to them in a later tutorial but here’s an example of a launch sequence:

Looping with ranges
def countdown = 10..0

for (i in countdown) {
    println "T minus $i and counting"
}

In the above example I store the range in the countdown variable in case I need it again later. If I don’t really need to re-use the range I can put the range’s literal value directly into the loop:

Looping with ranges refined
for (i in 10..1) {
    println "T minus $i and counting"
}

Useful Methods

We can use the size() method to find out how many elements are in the range:

The size method
def dalmations = 1..101
println dalmations.size()

As seen earlier, the getFrom() and getTo() methods return the start and final values respectively:

The range’s start and end values
def intRange = 1..10
println intRange.getFrom()
println intRange.getTo()

The isReverse() method returns true if a range iterates downwards (backwards):

Checking for reverse
def countdown = 10..0
assert countdown.isReverse() == true

You can can use the reverse() method to flip the range:

Reversing the range
def floors = 1..10
println floors.reverse()

In order to check if a value is contained within a range we use the containsWithinBounds method and pass it the value we’re checking on:

Checking bounds
def countdown = 10..0
assert countdown.containsWithinBounds(5) == true

The step method returns a list based on going through the range via the specified increment (step). In the example below I step through the range one at a time (step(1)) and then two at a time (step(2)):

Stepping
def countdown = 5..1
assert countdown.step(1) == [5, 4, 3, 2, 1]
assert countdown.step(2) == [5, 3, 1]

As step returns a list I can use it to populate a list variable that has too many numbers for me to be bothered typing out:

def dalmations = (1..101).step(1)
println dalmations

As we’re about to see the step method is very effective when used with closures.

Ranges and Closures

Closures are a method (function) that can be handled in a manner similar to variables. A closure is described within curly brackets {..} and can be passed as method parameters. Closure have a default variable named it and this holds a value passed to the closure by its caller.

We’ll look into closures much more thoroughly in a later tutorial but, for now, take in the following examples and refer back to them when you get to know closures a little better.

The step method will call a closure for each item in a range. In the example below I step through countdown one number at a time and, for each number, I display a message:

Stepping through a range with closures
def countdown = 10..1
countdown.step(1) {
    println "T minus $it and counting"
}

I can use the range literal but need to place it within (..):

Using the range literal
(10..1).step(1) {
    println "T minus $it and counting"
}

You can change the size of each step - in the case below I step down by 2 each time. Run the code and notice that launch never happens!

Changing the step
(10..1).step(2) {
    println "T minus $it and counting"
}

21. Regular Expressions

Regular expressions (RegEx’s) get entire books devoted to them and you’ll find some developers are RegEx ninjas and others (like myself) are RegEx numpties. This chapter will introduce the basics but the Java Tutorial’s Regular Expression trail is a useful reference as is Wikipedia for those seeking RegEx glory. There are also a number of online tools such as RegExr that come in very handy when trying to debug that elusive RegEx pattern.

To define the regular expression pattern we use the ~/ / syntax:

Declaring a regex
def regex = ~/\n/

Once stored as a variable, this regular expression can be used in a variety of ways. The example below sets up three string variables and tests them against the regex pattern by using the matches method - which returns true if the string matches the pattern:

Matching against a regex
def regex = ~/https?:\/\/.*/

def httpUrl = 'http://www.example.com/'
def httpsUrl = 'https://secure.example.com/'
def ftpUrl = 'ftp://ftp.example.com/'

assert httpUrl.matches(regex)
assert httpsUrl.matches(regex)
assert ! ftpUrl.matches(regex)

In the code above, ~/https?:\/\/.*/ is the regular expression pattern that’s essentially looking for any string starting with http or https. The s? will match 0 or 1 occurrence of s in the pattern. You’ll notice the odd-looking \/\/ - I need to escape the forward slashes in http:// so that Groovy doesn’t confuse them with the slashes used to define the regular expression pattern (~/../).

We’ll also look at the special operators for regular expressions in the section on Operators.

Underpinning Groovy’s regular expression functionality is the Java class java.util.regex.Pattern. Groovy handles the compiling of the pattern and this helps you focus on the struggle of getting the regular expression correct :)

Regular Expression Syntax

Regular expressions use a number of syntactic elements to define a pattern of text. We’ll take a brief look at them here.

Characters

These elements are used to match specific literal characters.

Literal characters
Element Matches
g The character g
\\ The backslash character
\t Tab character
\n Newline character
\f Formfeed character
\r Carriage-return character

In the example below I take a section of a poem and use the split method to get a list whose elements contain a single line from the poem.

Splitting a poem
// The Ballad of the Drover by Henry Lawson
def poem = '''\
 Across the stony ridges,
  Across the rolling plain,
 Young Harry Dale, the drover,
  Comes riding home again.
 And well his stock-horse bears him,
  And light of heart is he,
 And stoutly his old pack-horse
  Is trotting by his knee.'''

def regex = ~/\n/

def lines = regex.split(poem)

def i = 1
for (line in lines) {
    println "Line $i: $line"
    i++
}

Character Classes

Character classes are used to define character sets and sequences.

Character classes
Element Matches
[xyz] x, y or z
[^xyz] Not x, y or z
[a-zA-Z] Range of characters (all letters)
[0-9] Range of characters (all numbers)
[a-zA-Z_0-9] Range of characters
Predefined Character Classes

The predefined character classes save you from having to define the class specifically and are handy for seeking out words and whitespace.

Predefined character classes
Element Matches
. Any character
\d Digits [0-9]
\D Non-digits
\s Whitespace
\S Not whitespace
\w Word character [a-zA-Z_0-9]
\W Not a word character

Boundaries

Boundaries, to state the obvious, mark the edge of something - specifically a line or a word.

Boundaries
Element Matches
^ Start of a line
$ End of a line
\b Word boundary
\B Non-word boundary

Quantifiers

These determine how many matches are acceptable. For example s? matches the character s zero or one time - meaning that I expect that character to be an s or, if it’s not, move to the next part of the pattern. s+ means that I really want at least one s at that point.

Quantifiers
Element Matches
? Single match
* Zero or more matches
+ One or more matches
{n}? Exactly n matches
{n, }? At least n matches
{n,m}? At least n but not more that m matches

Useful Methods

A number of String methods can accept a regular expression and these are my preferred approach to checking text against regular expressions. Most of them take the pattern as the first parameter.

We saw the matches() method at the beginning of the chapter:

Matching
def regex = ~/https?:\/\/.*/
def httpUrl = 'http://www.example.com/'

assert httpUrl.matches(regex)

The find() method returns the first match against the pattern within the string. In the example below the find() will return the match against the port number in the URL:

Finding
def regex = ~/:[0-9]+/
def httpUrl = 'http://www.example.com:8080/'

println httpUrl.find(regex)

The findAll() method returns a list of matches for the pattern. In the example below I am returned all words in speech that start with like:

findAll
def speech = '''This like guy like I know but like don\'t really like
 was like so mean but likely to be nice when you know him better.'''

println speech.findAll(~/\blike\w*\b/)

Like, wow!

The example below provides a very basic word counter by seeking out the \b\w+\b pattern and displaying the size of the list returned by findAll:

A word counter
def poem = '''\
 Across the stony ridges,
  Across the rolling plain,
 Young Harry Dale, the drover,
  Comes riding home again.'''

def regex = ~/\b\w+\b/

println poem.findAll(regex).size()

The replaceFirst() and replaceAll() methods seek out matches and replace them in a manner that their names implies:

Replacing
def speech = '''This like guy like I know but like don\'t really like
 was like so mean but likely to be a nice guy when you know him better.'''

println speech.replaceAll(~/\blike\b/, 'um')
println speech.replaceFirst(~/\bguy\b/, 'marmoset')

The splitEachLine() method is very handy when handling structured files such as comma-separated files. You can see in the example below that the first parameter is the pattern that will match commas (~/,/) and the second parameter is a closure that will do something for each line. Within the closure, the it variable is a list with each element being the delimited segment of the text with the line:

Splitting
def csv = '''\
Bill,555-1234,cats
Jane,555-7485,dogs
Indira,555-0021,birds'''

csv.splitEachLine(~/,/) {
    println "Name: ${it[0]}"
}

Pattern Methods

The java.util.regex.Pattern class provides a number of useful methods. I prefer to use the String methods but maybe I’m just lazy.

The static matches method is called against Pattern to evaluate a pattern against a piece of text. You’ll note that the first parameter is the pattern but represented as a string so you drop the ~/../ notation:

Using Pattern
//Note the import
import java.util.regex.Pattern
assert Pattern.matches('https?://.*/', 'http://www.example.com/') == true

The matcher() method is called against a regular expression pattern and is passed the text that is to be checked. A Matcher variable is returned and these give you a whole heap of regular expression functionality. In my example I just check for the match by calling matches():

Using Matcher
def regex = ~/https?:\/\/.*/
def httpUrl = 'http://www.example.com/'
def matcher = regex.matcher(httpUrl)
assert matcher.matches() == true

The split() method uses a pattern as a delimiter and returns the elements of the parameter broken up by the delimiter. In my example below I split the domain up based on the period (.) delimiter:

Another split`
def regex = ~/\./
def domain = 'www.example.com'

println regex.split(domain)

That last example is simple but you can use some pretty funky patterns to split up a string.

22. Data types

Groovy does not preclude the programmer from explicitly declaring a data type, particularly when it would be pertinent to constrain the values being managed. Furthermore, knowledge of data types is very useful for a number of reasons:

  1. Use of JVM-compatible libraries may require knowledge of the data types required by method calls.
    • Important if you want to mine the rich collection of existing Java libraries
  2. Conversion between different data types (such as decimal numbers to whole numbers) can cause truncation and other (perhaps unexpected) results.
    • Essential knowledge if your program relies on calculations

Most of Java’s “core” classes (types) are defined in the java.lang package. Groovy enhances some of these in the GDK to give you extra flexibility.

Groovy’s use of types

The table below illustrates Groovy’s selection of a data type based on a literal value:

Groovy’s use of types
Value Assigned Type
true java.lang.Boolean
'a' java.lang.String
"This is a String" java.lang.String
"Hello ${Larry}" org.codehaus.groovy.runtime.GStringImpl
127 java.lang.Integer
32767 java.lang.Integer
2147483647 java.lang.Integer
9223372036854775807 java.lang.Long
92233720368547758070 java.math.BigInteger
3.14 java.math.BigDecimal
3.4028235E+38 java.math.BigDecimal
1.7976931348623157E+308 java.math.BigDecimal

It is important to note that the type is selected at each assignment - a variable that is assigned a string such as "Hello" is typed as java.lang.String but changes to java.lang.Integer when later assigned the value 101.

Using a specific type

A variable can be declared as being of a specific data type. When using a type, drop the def keyword:

Declaring a variable using a specific type
Integer myNum = 1
String myName = "Fred nurk"

Suffixes can also be used if you want to be really specific about the data type Groovy is to use for a number. When using suffixes you use the def keyword to define the variable: def dozen = 12i

Type suffixes supported by Groovy
Suffix Type Example
I or i Integer 12i
L or l Long 23423l
F or f Float 3.1415f
D or d Double 3.1415d
G or g BigInteger 1_000_000g
G or g BigDecimal 3.1415g

You may have noticed that BigInteger and BigDecimal have the same suffix - this isn’t a typo - Groovy works out which one you need simply by determining if the number is a whole number (BigInteger) or a decimal (BigDecimal).

If you’re going to use explicit types then you need to know limitations of that type. For example, the following code will fail:

assert 3.1415926535f == 3.1415926535d

This failure occurs because Float will shorten (narrow) the value to 3.1415927 - not a mistake you’d want to make when measuring optics for your space telescope! You can see which type Groovy will use automatically by running this snippet of code:

println 3.1415926535.class.name

The null Value

Variables that are not assigned a value at declaration are provided a null value by default. This is a special reference that indicates the variable is devoid of a value.

Variables can be explicitly assigned null:

def id = null

Available data types

As Groovy imports the java.lang package as well as the java.math.BigDecimal and java.math.BigInteger classes by default, a range of data types are available for immediate use:

  • Boolean: to store a logical value of true or false
  • Numbers (based on java.lang.Number):
    • Byte
    • Short
    • Integer
    • Long
    • Float
    • Double
    • BigDecimal
    • BigInteger
  • Character: A single character such as a letter or non-printing character
  • String: A regular Java-esque piece of text
  • GString: A Groovy string that allows for interpolation
  • Object: This is the base class for all other classes
  • Closure: The class that holds closure values

The types listed above are often referred to as reference types, indicating that they relate to a class definition. Groovy also provides a set of primitive types that are more closely aligned to the C programming language than an object-oriented language such as Java and Groovy.

Primitive types

The table below maps the types defined in java.lang against their equivalent primitive types:

The primitive types
Type Primitive type Value range Size (bits)
Boolean boolean true or false -
Byte byte -128 to 127, inclusive 8
Short short -32768 to 32767, inclusive 16
Character char ‘\u0000’ to ‘\uffff’ inclusive 16
Integer int -2147483648 to 2147483647, inclusive 32
Long long -9223372036854775808 to 9223372036854775807, inclusive 64
Float float 32-bit IEEE 754 floating-point numbers 32
Double double 64-bit IEEE 754 floating-point numbers 64

You can check those value ranges by using the MIN_VALUE and MAX_VALUE constants available on the various classes representing numbers:

Determining value range
println Integer.MIN_VALUE
println Integer.MAX_VALUE
println Float.MIN_VALUE
println Float.MAX_VALUE

As an object-oriented language Groovy also provides a mechanism for declaring new data types (objects) that extend and encapsulate information to meet a range of requirements. These implicitly extend the java.lag.Object class.

Type Conversions

Groovy will convert values assigned to variables into the variable’s declared data type. For example, the code below declares a variable of type “String” and then assigns it 3.14 (a number). The assertion that the variable remains of type “String” will succeed, indicating that 3.14 was converted to a String value by Groovy before being assigned to the myName variable.

Checking the type
String myName = "Fred nurk"
myName = 3.14
assert myName.class == java.lang.String

Care must be taken to not rely totally on this automatic conversion. In the example below the assertion will fail as the myPi variable is declared as an Integer and the assignment drops the fractional component of 3.14:

def pi = 3.14
Integer myPi = pi
assert myPi == pi

Casting

The as operator can be used to cast (change) a value to another class.

Casting
def pi = 3.1415926535 as Integer
assert 3 == pi

This will be discussed further in the Object Operators tutorial.

Converting Numbers

java.lang.Number provides a number of methods for converting numbers between the various numerical data types:

  • byteValue()
  • doubleValue()
    • also toDouble()
  • floatValue()
    • also toFloat()
  • intValue()
    • also toInteger()
  • longValue()
    • also toLong()
  • shortValue()
  • toBigInteger()
  • toBigDecimal()

Here’s a small example of grabbing the whole (integer) component from a number:

Getting the integer
def pi = 3.1415926535
assert 3 == pi.intValue()
assert 3 == pi.toInteger()