Groovy is a scripting language based on Java.
It can be downloaded from
http://groovy.codehaus.org..
Not surprising, Groovy pre-compiler is also written in Java and Java must be installed first in order to run Groovy.
The Theory in One Sentence
What if we redesign Java to be more like Perl, but looking like Java so Java developers wouldn't have to learn Perl, would developers be more productive?
Pros
- Looks like Java.
- Can access all Java classes.
- More intuitive than Java: good for scripts and prototyping.
- Good scripting features (compared to Java).
Cons
- Poor language vision and design.
- More error-prone (than Java).
- Poorer readability (than Java).
- Possibly less scalable and maintainable (than Java). Doesn't always honour "private", etc.
- Uses more resources than Java (especially closures that cause the environment to persist).
- Lack of good, organized documentation with real-world examples.
Groovy Terminology
Auto-boxing - converting a primitive data type to its equivalent object without an explicit type cast
Closure - a lamda function with a parental context
Groovlets - servlets using Groovy
Meta-programming - using Groovy to investigate and modify a program while the program is running.
Running Groovy
You can start the interactive Groovy Console by typing
$ groovyConsole &
or you can edit a groovy script in your favourite text editor and run the script from the shell with
$ groovy myscript
Or try a small piece of Goovy with
$ groovy -e "statements" arguments
Groovy scripts can also be embedded in JSP documents as Groovy servlets ("groovlets") by loading a special Groovy Java class. Similar techinques can be used for distributed objects (Groovy Beans), or text/html templates.
A First Groovy Script
Here is a very simple example of Groovy in action.
class Foo {
int i = 2;
void print_i() {
println "The value of i is " + i;
}
}
Foo f = new Foo();
f.print_i();
Script: A First Groovy Script
The value of i is 2
![[screenshot]](groovy_1.png)
Figure: Screenshot of Script 1 in the Groovy Console
You can also create the script with a text editor and run it from the shell prompt. If the name of the script file is "first.gvy", you can run it this way:
$ groovy first.gvy The value of i is 2
Let's look at the components of this script. These concepts should be familar to you if you've used the Java programming language.
- class Foo - this defines a new class called "Foo"
- int i - Foo contains a variable named "i"
- void print_name - Foo also contains a function called "print_name"
- Foo f = new Foo() - f is a new object which as the type of "Foo"
- f.print_name - execute the print_name function in f, showing the value of i (which is 2)
- Semi-colons are optional
Notice in Groovy that some features of Java are optional: Groovy doesn't require "System." before println and it doesn't require paratheses around println's parameter. You can use them if you want to.
// Four ways to say "hello world" System.out.println( "hello world" ); println ( "hello world" ); println ( "hello world" ) println "hello world"; println "hello world"
Which way is the "right" way?
Data Types
There are 7 primitive data types in Groovy (Java has 5). That is, Groovy provides ways of creating literals for these types and all other types are built from these types:
- boolean - x = true
- integer - x = 1
- float x = 15.2
- string x = "apple" ; x2 = /blueberry/
- classes x = new widgit()
- lists x = ["apple", "blueberry", "cherry"]
- maps (an associative array) x = ["apple":10, "blueberry":15, "cherry":20]
Lists and maps are extensions added to Java. They are objects so there are built-in functions availble to tell you more about the objects. The keyword null is used to represent a missing value.
// File: hash.groovy fruit_total = [ "apple":10, "blueberry":15, "cherry":20 ]; println "The fruit_total map looks like this: " + fruit_total; println "The class of the map is " + fruit_total.getClass(); println "The size of the map is " + fruit_total.size(); println "The size of an empty map is " + [:].size(); println "The total number of apples is " + fruit_total["apple"]; fruit_total["blueberry"] = 25; println "The total number of blueberries is now " + fruit_total["blueberry"]; println "The total number of snozberries is " + fruit_total["snozberry"];
$ groovy hash.groovy The fruit_total map looks like this: [apple:10, blueberry:15, cherry:20] The class of the map is class java.util.LinkedHashMap The size of the map is 3 The size of an empty map is 0 The total number of apples is 10 The total number of blueberries is now 25 The total number of snozberries is null
Groovy will perform value substitutions when it finds a dollar sign in a double-quote string.
Type conversions are achieved with "(type) expr".
Arbitrary expressions can be put in a double-quote string with ${..}. The evaluation doesn't happen when the string literal is defined but when the string is used.
//File: sub.groovy total = 15; print "The total is $total";
$ groovy sub.groovy The total is 15
Variables
Variables (unless they are in the main program) must be declared. If they are delcared with def, the variable type is not defined. If they are declared with a type (a primitive data type or a class), they must contain values of that type.
x = true println x
Java: "Auto Boxing" means automatically converting a primitive to its equivalent object, such as an int to an Integer, without an explicit clast.
println 2.getClass();
class java.lang.Integer
In Java, you would have to convert 2 to an Integer object first.
Constants are declared with final.
Arrays
Java: array literals use square brackets like list literals so as not to conflict with closures, not curly brackets as in Java.
Operators
All your standard Java operators are supported:
Arithmetic Operators
+ (addition or concetenation), - (subtraction), * (multiplication), / (division), % (modulo), ++ (increment), -- (decrement)
Boolean/Logical Comparison
==, !=, >, >=, <, <=, ==~ (regular expression match)
Java: == with classes will compare values, not object identity as in Java
Boolean/Logical Operators
&& (and), || (or), ! (not)
Bitwise Operators
& (and), | (or), ~ (not/complement), ^ (exclusive or), << (bit shift left), >> (arithmetic/signed bit shift right), >>> (logical/unsigned bit shift right)
Assignment and Reflexive Assignment Operators
= (assignment), +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>=
Other Operators
? : (ternary), . (object access), [] (array access), new (object creation), instanceof (type comparison)
// Regular expressions
def fruit = "apple"
if ( fruit ==~ /^a[a-z]*/ ) {
println "It looks like it might be an apple"
} else {
println "It doesn't look like apple"
}
It doesn't look like apple
Flow of Control
if ( expression ) statement;
if ( expression ) statement else if statement else statement;
switch (item) case value-list|value-range|value-regex| type-list...default:..}
for (var in in-expr) statement;
for (init-expr; test-expr; incr-expression) statement
while ( expr ) statement
do statement while ( expression );
Loops may be named with a proceeding label with a colon.
Loop exit statements are break and continue. These may have a loop label to exit multiple loops.
Functions
Functions can have a return value or can be declared with def for no specific return value.
Values that are not assigned are implicitly used as a return value. A return statement is not required (it is implied).
// implied_return
int one() { 1 } // implies "return 1"
println one()
1
// function that returns an int or string
def divide( int d ) {
( d != 0 ) ? 100/d : "undefined";
}
println divide( 5 )
println divide( 0 )
2E+1 undefined
An array values can be converted to an actual parameter list with a leading asterisk.
The biggest difference between a Groovy function and a Java function is that the calls declared in the function are not checked until the function is executed.
For example, in Java this is an error:
public static void main( String[] args) {
// This is an error in Java...get must exist at compile-time!
get( "I don't exist" );
}
But in Groovy it isn't.
private static void some_function() {
// OK in Groovy
get( "I don't exist" );
}
get() doesn't need to exist when the function is defined but it must exist in scope when the function runs.
some_function() # exception thrown if get doesn't exist
This behavior only applies to function calls. Variables come from where the function is defined.
Dynamic Function Calls
Functions are called by name. However, functions may also be called dynamically with a name in a variable through string expansion. The function name must be in quotes
String s = "one"
def one() { "one" }
println "$s"()
class Numbers {
String one() { "one" }
String two() { "two" }
}
Numbers numbers = new Numbers();
s = "one"
println numbers."$s"()
one one
Putting the class name prefix within the quotes will not work. In the above example, s = "numbers.one" will not work. Nor can you make the containing class name dynamic.
Closures
Closures are anonymous (unnamed) functions assigned to variables. That is, lamda functions. They have the same property as Goovy functions: there are no errors if calls inside the closure don't exist when the closure is created.
// Closures
def some_function = { 2 * 2 }
println some_function
println some_function()
some_function = { 2 * it } // it is a parameter
println some_function( 3 )
However the main purpose of closures is to pass these anonymous functions to routines that require a function callback. You can create a function without having to give the function a name.
def fruit = ['apple', 'banana', 'pear' ];
fruit.collect { println 'The fruit is ' + it }
The fruit is apple The fruit is banana The fruit is pear Result: [null, null, null]
These will also work:
fruit.collect( { println 'The fruit is ' + it } );
print_fruit2 = { println 'The fruit is ' + it }
fruit.collect print_fruit2
Why doesn't this work:
public void print_fruit( String f ) {
println( 'The fruit is ' + f );
}
// fruit.collect( print_fruit );
Closures see the parent's variables, even if they are private. A side-effect of groovy programs is they use more memory because they must keep the parent's context around for the duration of the closure.
// Closures see the parent's variables
private int i = 3;
def c = { println "i is " + i; };
c(); // prints "i is 3"
Java: return is not required.
Exceptions and Asserts
Exceptions are handled with try, catch and an optional finally.
try {
throw new Exception( "die!" )
} catch (e) {
println "The exception was caught: " + e.getMessage();
} finally {
println "Nothing to tidy up after a catch"
}
The exception was caught: die! Nothing to tidy up after a catch
Catch statements can set the function return value.
Assertions will throw an exception if the expression is false.
assert 1 == 1
Groovy Configuration File
By default, Groovy provides (uses) the following packages: java.io.*, java.lang.*, java.math.BigDecimal, java.math.BigInteger, java.net.*, java.util.*, groovy.lang.* and groovy.util.*. You do not include a use statement to use these packages.
Groovy can load Java JAR archives to make those classes available to your scripts. The Groovy configuration file (usually /usr/share/groovy/conf/groovy-starter.conf in Linux) lists directories were JAR files will be loaded from.
Misc Observations
// File: nodecl.groovy total = 15; println "The total is $total"; int total = 15; // Total already exists but is allowed println "The total is $total"; int total = 15; // Duplicate detected at run-time println "The total is $total"; def total = 15; // Duplicate not detected until here at compile time println "The total is $total";
Optional things that really aren't optional: cases where def, semi-colons, etc. are required. Long lines broken into two lines--the second line may be ignored because semi-colons are not required (Groovy doesn't know it's one statement).
Why "def" and not "typeless" or "multitype"?
Groovy's typing short-cuts result in readability problems.
Missing things are not errors.
To use Java classes, still have to work with Java's bureaucratic class conventions.
If they got rid of semi-colons, why not get rid of statement blocks and be more like Python and Turing?
Errata
Groovy supports documentation annotations.
Groovy has classes to examine programs while they are running, such as being able to add new functions to an object that already exists.
Groovy supports generics.
![[Navigation Bar]](../art/nav_bar_head2.png)