Groovy - ilya-khadykin/notes-outdated GitHub Wiki

Installing Groovy

Prerequisite: JDK should be already installed

Download applicable distribution from http://groovy-lang.org/download.html

And set GROOVY_HOME environment variable accordingly

You can also use Groovy enVironment Manager (UNIX)

Ways to run Groovy code

You can compile it with groovyc compiler or you can also run scripts without compiling in groovysh

"Hello, World!" in Groovy

void sayHello(String name) {
  println "Hello, ${name}!"
}

sayHello('Dolly')

Tools

  • groovyc - compiler
    > groovyc hello_world.groovy // creates hello_world.class
    NOTE: The Groovy compiler knows how to compile Java, too
  • groovy - command for running Groovy code
    > groovy -e 'println InetAddress.localHost' // prints name and IP address of host system
    groovy hello_world.groovy
  • groovysh - groovy shell
  • groovyConsole - a simple IDE distributed with the language (something similar to Python`s IDLE)

The Language

Idioms

  • last evaluated expression is always returned by default, no return needed
String toString() { 
   "$classField1 $classField2" 
}
  • if closure is the last parameter of a method you can specify it outside of parentheses
10.downto(7) { println it }

Automatic imports

In Java, if you don’t add any import statements you get java.lang.* automatically.

In Groovy, if you don’t add any import statements, you get:

java.lang.*
java.util.*
java.net.*
java.io.*
java.math.BigInteger
java.math.BigDecimal
groovy.lang.*
groovy.util.* 

Groovy classes therefore have far fewer import statements than Java classes.

Operator Overloading

In Java, the only overloaded operator is +, which means addition for numerical values and concatenation for strings.

In Groovy, all operators invoke methods. The complete list of operators is given on http://groovy.codehaus.org/Operator+Overloading. Here is an abbreviated version.

Table with Operators and Corresponding Methods

Operator Method call
a + b a.plus(b)
a - b a.minus(b)
a * b a.multiply(b)
a / b a.div(b)
a % b a.mod(b)
a ** b a.power(b)
a++ a.next()
a-- a.previous()
a[b] a.getAt(b)
a[b] = c a.putAt(b, c) a == b a.equals(b) or a.compareTo(b) == 0

While overloading operators in regular Groovy programs is not that common, it’s used throughout the standard libraries.

For example, the java.lang.Number class is the superclass of all the wrapper classes as well as java.math.BigInteger and java.math.BigDecimal. The Groovy JDK adds plus, minus, and others to Number, which allows the operators to be used with all of its subclasses.

assert

Java has an assert keyword that is rarely used and turned off by default.

Groovy uses a "power assert" that returns a lot of information when it fails

int x = 3 
int y = 4 
assert 7 == x + y // true, so returns nothing 
assert 7 == x + y + 1

Assertion failed:
assert 7 == x + y + 1
          |  | | | |
          |  3 7 4 8
          false

All that extra debugging information means most Groovy developers use assert in all their tests

The def keyword

Groovy is optionally typed. If you declare a variable to be of type String, or Date, or Employee, then that’s all that can be assigned to them.

Integer x = 1 
x = 'abc' // throws ClassCastException

The def keyword tells the compiler we are not declaring a type, which will be determined at runtime.

def x = 1 
assert x.getClass() == Integer

x = new Date() 
assert x.getClass() == Date

x = 'abc' 
assert x.getClass() == String

In Grails, properties in domain classes — which are mapped to database tables — require actual data types. The def keyword is often used as the return type on controller methods, however.

Numbers

Groovy uses the same numeric types as Java, but there are no primitives in Groovy. Instead, Groovy uses the wrapper classes (like Integer, Double, and Boolean) for any primitive values.

The default type for a non-floating point literal is Integer or longer:

Non-floating point literals

assert 3.getClass() == Integer
assert 33333333333333.getClass() == Long
assert 33333333333333333333333.getClass() == BigInteger

Groovy uses BigDecimal for all floating-point literals and arithmetic calculations. Floating-point values

(2.5).getClass() == java.math.BigDecimal
(2/3).getClass() == java.math.BigDecimal

Unlike Java, division is done at BigDecimal levels even if both arguments are integers. If you want integer division, use the intdiv method in java.lang.Number.

Strings and Groovy Strings

Groovy has two types of strings, single- and double-quoted:

  • Single-quoted strings are instances of java.lang.String.
  • Double-quoted strings are Groovy strings and allow interpolation:
def s = 'this is a string' 
assert s.getClass() == String

s = "this uses double-quotes but is still a String" 
assert s.getClass() == String

s = "this string uses interpolation: ${1 + 1}" 
assert s == 'this string uses interpolation: 2'
assert s instanceof GString

The ${…} notation inside a double-quoted string evaluates its contents as a Groovy expression and invokes toString on the result. If you are evaluating a variable only, you can leave out the braces.

String first = 'Graeme'
String last = 'Rocher' 
assert "$first $last" == 'Graeme Rocher'

Groovy also supports multiline strings. Three single quotes are regular multiline strings.

def picard = '''
Oh the vaccuum outside is endless 
Unforgiving, cold, and friendless 
But still we must boldly go 
Make it so, make it so, make it so!
'''
This string has five lines, because the first line starts with a carriage return.

Three double-quotes are multiline Groovy strings.

```groovy
def saying = """
There are ${Integer.toBinaryString(2)} kinds of people in the world
Those who know binary, and those who don't
"""
Multiline strings are helpful in many situations, but they are particularly useful when executing SQL statements.

Finally, Groovy supports what are called _slashy_ strings, which are delimited by forward slashes.

__Slashy strings for regular expressions__
```groovy
def zip = /\d{5}(-\d{4})?/
assert '12345' ==~ zip
assert '12345-1234' ==~
zip assert '12345 12345-1234 1234'.findAll(zip) ==
     ['12345', '12345-1234']

Slashy strings do not require you to use double backslashes inside regular expressions. Here the \d pattern represents a decimal digit. The pattern \W means any non-word character (i.e., other than [azA-Z0-9_]).

def testString = 'Flee to me, remote elf!'.toLowerCase().replaceAll(/\W/,'')
assert testString == 'fleetomeremoteelf'
assert testString == testString.reverse() // test string is a palindrome

The ==~ operator checks for an exact match and returns the boolean true or false.
The =~ operator returns an instance of java.util.regex.Matcher.

Using a tilde on a slashy string turns it into an instance of java.util.regex.Pattern, as in

assert ~/abcd/ instanceof java.util.regex.Pattern

A more detailed discussion of regular expressions in Groovy can be found at http://groovy.codehaus.org/Regular+Expressions.

POGO (Plain Old Groovy Object)

import groovy.transform.*
@EqualsAndHashCode // Groovy Annotation generates .equals() and .hashCode()
@TupleConstructor
//@Cannonical generates toString(), equals(), hashCode() and tuple constructor
class Person {
   String first
   String last

   String toString() { "$first $last" }
}

Person p = new Person()
p.setFirst('David')
p.last = 'Ortiz' // calls setter method, does not violates encapsulation 
println "${p.getFirst()} ${p.last}"

Person pConstructor = new Person(first: 'Name', last: 'Surname') // calls setters using provided Map
println pConstructor.toString()

for in loop

for (n in nums) {
    println n // def is assumed
}

Collections

Range

Range r = 1..10
println(r)
println r.from
println r.to
print r.contains(10)
r = 1..<10
println(r)

List

List nums = [3, 1, 4, 1, 5, 9, 2, 6, 5]
List numsLinkedList = [3, 1, 4, 1, 5, 9, 2, 6, 5] as LinkedList
println nums
println nums.class.name
nums -1 -5
nums * 2
nums + [3, 5]
nums << 100

Map

def map = [a:1, b:2, c:2]
map.put('d', 3)
map['e'] = 1
println map
println map.class.name // throws Exception since dot '.' operator has been already overloaded
println map.getClass().name // LinkedHashMap

Set

List numsSet = [3, 1, 4, 1, 5, 9, 2, 6, 5] as Set // as SortedSet
println numsSet 
println numsSet.class.name

Closures

List nums = [3, 1, 4, 1, 5, 9]
nums.each { 
   println it // 'it' is a default name for a parameter
}

nums.each { n ->
   println n
}

nums.eachWithIndex { n, idx ->
    println "nums[$idx] == $n"
}

Map m = [a:1, b:2, c:2]
m.each { e ->
    println "m[${e.key}] == ${e.value}"
}
m.each { k, v ->
    println "m[$k] == $v"
}

10.downto(7, { println it } ) // Java style
10.downto(7) { println it } // Idiomatic Groovy
10.downto 7, { println it } // optional parentheses

map/reduce in Groovy

List nums = [1, 2, 64, 4, 3]
nums.collect { it * 2 } // map
    .findAll { it % 3 == 0 } // filter
    .sun() //reduce

Special Operators

*. operator

*. applies to every element of the collection

List strings = 'this is a list of strings'.split()
prinln strings.collect { it.size() }
println strings*.size() // equivalent to strings.collect wit closure
println strings.size()

?. Null Safety

?. returns null if some property is null and does not throw NullPointerException

<=> Spaceship

int x = 3
int y = 6
int z = 8
println x <=> y // invokes compareTo()
println y <=> y
println z <=> y

?: Elvis

Groovy Truth

In Groovy, all the following are true:

  • Non-zero numbers
  • Non-null references
  • Non-empty strings
  • Non-empty collections
  • Regular expressions with a match
  • Boolean true
String name
String n = (name != null && name.size() > 0 ? name: 'World')
n = name ? name : 'World'
n = name ?: 'World' // equivalent to the above 
println "Hello, %n!"

Parse json using JsonSlurper

Here is an example of how to do it (copied from groovyConsole):

import groovy.json.JsonSlurper 

def jsonSlurper = new JsonSlurper()
def contents = jsonSlurper.parseText('https://raw.githubusercontent.com/exercism/java/master/config.json'.toURL().text)

def exercises = []
contents.exercises.each{ 
 if (!it.deprecated) {
 exercises.add(it.slug)
 }
}

exercises

References

Special thanks to Ken Kousen

⚠️ **GitHub.com Fallback** ⚠️