Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

The code snippets in these tutorials use comments to explain things:

Code Block

//comment like this to end of line, ignoring */ and /* and ' and "
/*or comment like this, ignoring // and ' and " until: */
/*or comment over 
many lines, /*with no nesting*/

Groovy code can contain strings:

Code Block

'A string can be within single quotes on one line...'
'''...or within triple single quotes
over many lines, ignoring // and */ and /* comment delimiters,...'''
"...or within double quotes..."
"""...or within triple double quotes
over many lines."""

Each line here does the same:

Code Block

println 'hello, world' //the function 'println' prints a string then newline
print 'hello, world\n' //'print' doesn't print newline, but we can embed
                       //newlines ('\n' on Unix/Linux, '\r\n' on Windows)
println 'hello' + ', ' + 'world' // + joins strings together
print 'hello, '; println 'world'
                       //use semi-colons to join two statements on one line
println( 'hello, world' )
             //can put command parameter in parens, sometimes we might have to
def a= 'world'; println 'hello, ' + a
                       //'def' to define a variable and give it a value
print 'hello, world'; println()
  //empty parens must be used for no-arg functions; here, prints a blank line
def b= 'hello', c= 'world'; println "$b, ${c}"
                       //$ in print string captures variable's value

We can also assign integers and decimals to variables:

Code Block

def g = 7, groovy = 10.2
  //we can separate more than one defined variable by a comma
print g + ', ' + groovy + '\n' //prints: 7, 10.2
assert g + ', ' + groovy == '7, 10.2' //we can use assert statement and == 
                                      //operator to understand examples

We can use operators like + - * / and parentheses ( ) with numbers, following usual math grouping rules:

Code Block

assert 4 * ( 2 + 3 ) - 6 == 14 //integers only
assert 2.5 + 7 == 9.5
assert 7 / 4 == 1.75 //decimal number or division converts expression to decimal

We can use the operators == > < >= <= != with numbers, the values true and false, the operators ! (not), && (and), and || (or), all with parentheses, to produce boolean expressions:

Code Block

assert 2 > 3 == false
assert 7 <= 9
assert 7 != 2
assert true
assert ! false
assert 2 > 3 || 7 <= 9
assert ( 2 > 3 || 4 < 5 ) && 6 != 7

Variables are versatile:

Code Block

def a
assert a == null
  //variables defined but not given a value have special value null
def b = 1
assert b == 1
b = 2
assert b == 2 //variables can be re-assigned to
b = 'cat'
assert b == 'cat' //they can be re-assigned different types/classes of data
b = null
assert b == null //they can be unassigned

All names in Groovy, including variable names, can contain any alphabetic character or the underscore, and contain any digit not in first position:

Code Block

def abc= 4
def a23c= 4
def ab_c= 4
def _abc= 4

def ABC= 4
assert abc == ABC //although their values are the same...
assert ! abc.is( ABC ) //...the variables 'abc' and 'ABC' are different,
                       //the names being case-sensitive

/*these each produce compile errors when uncommented...
def abc //already defined
def a%c= 4 //not a valid name because it contains a symbol other than _
def 2bc= 4 //may not contain a digit in first position
*/

All data in Groovy is built from "classes" and instances of them. Class names by convention begin with an uppercase character:

Code Block

assert Byte.MAX_VALUE == 127
  //a class can have attached variables, called 'fields'
assert Byte.parseByte('34') == 34
  //a class can have attached functions, called 'methods'
def b= new Byte('34')
  //we can create an 'instance' of a class using the 'new' keyword
assert b.intValue() == 34
  //each instance can also have attached fields and methods

We can inspect the class of any entity, such as numbers and strings, using the class field:

Code Block

assert 4.class == Integer //the common types have both a short name...
assert 4.class == java.lang.Integer //...and a long name
assert 4.5.class == BigDecimal
assert 'hello, world'.class == String
def a= 7
assert a.class == Integer

There are many predefined classes in Groovy, but only the most common ones are always visible to Groovy code. Most need to be qualified with a "package" name, eg, 'java.text.DecimalFormat', or the package must be imported beforehand:

Code Block

import java.text.*
assert new DecimalFormat( '#,#00.0#' ).format( 5.6789 ) == '05.68'

Or:

Code Block

assert new java.text.DecimalFormat( '#,#00.0#' ).format( 5.6789 ) == '05.68'

If a line can be interpreted as a valid statement, it will be:

Code Block

def i=
1 //because 'def i=' isn't a valid statement,
  //the '1' is appended to the previous line

//a compile error when uncommented: 'def j' is valid, so is interpreted as
//a statement. Then the invalid '= 1' causes the error...
/*
def j
= 1
*/

def k \
= 1 //a backslash ensures a line is never interpreted as a standalone statement

Sometimes code in a script doesn't compile: we comment it out in our examples. Other code compiles but generates a "checked exception" which we can catch and handle:

Code Block

try{
  'moo'.toLong() //this will generate an exception
  assert false 
      //this code should never be reached, so will always fail if executed
}catch(e){ assert e instanceof NumberFormatException }
  //we can check the exception type using 'instanceof'

We can use square brackets [ ] to represent both ordered lists and key mappings:

Code Block

def list= [1, 2, 3]
list= [] //empty list
list= [1, 'b', false, 4.5 ] //mixed types of values OK
assert list[0] == 1 && list[1] == 'b' && ! list[2] && list[3] == 4.5
  //we can refer to items individually by index

def map= [1:'a', 2:'b', 3:'c'] //map indicated with colon :
map= [:] //empty map
map= ['a': 1, 'b': 'c', 'groovy': 78.9, 12: true] //mixed types of values
assert map['a'] == 1 && map['b'] == 'c' && map['groovy'] == 78.9 && map[12]
  //we can refer to values individually by key

'each' tells the code following it to execute for each item in a list or map:
//for every item in list, assign to 'it' and execute the following code...
[ 2, -17, +987, 0 ].each{
  println it
}
//we can specify a different name for the argument other than the default...
[ 2, -17, +987, 0 ].each{ n -> 
  println n
}
//we can specify two or more arguments, as with this map...
[ 1: 3, 2: 6, 3: 9, 4: 12 ].each{ k, v-> 
  assert k * 3 == v
}

We can specify a list as a 'range', ie, by only the first and last items:

Code Block

( 3..7 ).each{ println it } //prints numbers 3, 4, 5, 6, and 7
( 3..<7 ).each{ println it } //prints numbers 3, 4, 5, and 6 //excludes 7

We can convert data of one type to another using the 'as' keyword:

Code Block

assert ('100' as Integer) == 100

Sometimes, we need to use a more efficient type of list known as an array, where the type of each element must be the same. Arrays can't be represented directly in the syntax, but we can convert a list to one easily:

Code Block

def x = ['a97', 'b98', 'c99'] as Integer[] //convert each item in list to an Integer
assert x[0] == 97 && x[1] == 98 && x[2] == 99 //access each element individually

We can choose between two execution options using the if-else-statement:

Code Block

def a= 2
if( a < 5 ){
  println "a, being $a, is less than 5."
}else{
  assert false //this line should never execute
}

We can execute some code a certain number of times:

Code Block

def i=0
10.times{ println i++ } //increment i by 1 after printing it

//another less declarative style of looping...
while( i > 0 ){
  println i-- //decrement i by after printing it
}

We can enclose code in parentheses and execute it later. The enclosed code is called a "closable block" or "closure":

Code Block

def c= { def a= 5, b= 9; a * b }
assert c() == 45

[ { def a= 'ab'; a + 'bc' },
  { 'abbc' },
].each{ assert it() == 'abbc' }

We can spawn new threads from our main thread:

Code Block

def i=0, j=0
def f= new File('TheOutput.txt') //create or overwrite this file
Thread.start{
  while(true){
    i++
    if(i%1000 == 0) f<< 'S' //spawned thread
  }
}
while(true){
  j++
  if(j%1000 == 0) f<< 'M' //main thread
}

...