Groovy Introduction

Preparation

This project illustrates how to install a pre-existing Groovy project in NetBeans and demonstrates some of the basic features of the Groovy language.

Install the Groovy/Grails plugin

Although we do not need Grails at this point, it's the only way to get the Groovy features installed into NetBeans.

Open Tools → Plugins, selecting the Available Plugins tab. Look for the plugin Groovy and Grails in the Groovy category. Click the checkbox and then the Install button. Complete the installation and restart NetBeans.

Project Install and Run

Make sure you have the NetBeansProjects directory created. You can do so by going through the JDK/NetBeans installation document, creating a "Hello World" Java project. Just creating a fresh project will pre-create the NetBeansProjects directory.

Download the source archive GroovyIntro.zip. Extract it (as the GroovyIntro folder) into the default NetBeans application folder:
Documents\NetBeansProjects\     (Windows)
~/NetBeansProjects/             (Mac OSX & Linux)
Open NetBeans. There is no official "Groovy Project" with or without existing sources. In fact any Java project can be a Groovy project because Java and Groovy share a common compiled byte code. The trick is getting NetBeans to recognize and know what to do to the files with the .groovy extension.

Install as a Java project with existing sources

  1. Create a New Project and select Java Project with Existing Sources, click Next. In the Name and Location dialog, the most import thing is to get the correct
    Project Folder: 
    You can do this in two ways:
    1. Use the Browse button and set the Project Folder correctly, then set the Project Name to what you like.
    2. Set the Project Name first, exactly to GroovyIntro and have the Project Folder be set automatically to the correct thing. (it must be the name of the project folder) and
    Click Next.
  2. In the Existing Sources window, click the Add Folder... button. Make sure that it is open to the NetBeansProjects/GroovyIntro folder with the src folder visible. Select src. Then OK and, back in previous window, Finish.

Add "Groovy Nature" to the project

Right-click on the "Source Packages" in the Projects window selecting:
New → Groovy Class
The first time you may need to dig this choice out of the menu by:
New → Other → Groovy → Groovy Class
Keep the default settings with name NewGroovyClass, click Finish.

Just by creating it you've done all that is necessary. Now simply right-click and delete the NewGroovyClass.groovy file.

Project Execution

This project is simply a series of main classes meant to be run and observed individually:
MainJava.java
MainGroovy.groovy
MainClosures.groovy
MainDataStructures.groovy
To run the individual files, simply right-click the file and select:
Run File
Observe the display in the Output window, matching up the output with the code.

Java/Groovy Beans and access

So-called bean classes use specific getter and setter methods to express exposed properties. These methods employ a strict syntax. One or both of these methods are employed:
public JavaType getSomeProperty() { ... }
public void setSomeProperty(JavaType ...) { ... }
in order to expose the property:
JavaType someProperty
The type signatures and camel-cased names must follow this precise formula in order to correctly expose the property. A variation is made for boolean properties in which the getter method is "boolean isSomeProperty" i.e., "is" replaces "get".

This Java class exposes the properties a, b, c, and d:

BeanJava.java
package groovyintro;
 
public class BeanJava {
  // get-set
  private int a;
  public int getA() {
    return a;
  }
  public void setA(int a) {
    this.a = a;
  }
 
  // get only
  private int b = 77;
  public int getB() { return b; }
 
  // set-only
  private int c;
  void setC(int c) { this.c = c; }
 
  // get-set without associated data member
  public int getD() { return 99; }
  public void setD(int d) { }
 
  // read-write
  public int e;
}
The e data member expresses something rarely done in Java, a "naked" public data member, so to speak. The equivalent Groovy class is the following:

BeanGroovy.groovy
package groovyintro
 
class BeanGroovy {
  // get-set
  int a;
 
  // get only
  private int b = 77
  public int getB() { b; }
 
  // set only
  private int c;
  public void setC(int c) { this.c = c }
 
  // get-set without obvious data member
  public int getD() { return 99; }
  public void setD(int d) { }
 
  // read-write
  public int e;
}
The following file illustrates usage of both the Java and Groovy bean objects, suggesting their equivalence. The commented statements illustrate expected compile-time errors.

MainJava.java
package groovyintro;
 
public class MainJava {
  public static void main(String[] args) {
    BeanJava jbean = new BeanJava();
 
    jbean.setA(1);
    System.out.println("jbean.a: " + jbean.getA());
//    jbean.a = 1;
//    System.out.println(jbean.a);
 
//    jbean.setB(2);
    System.out.println("jbean.b: " + jbean.getB());
 
    jbean.setC(3);
//    System.out.println( jbean.getC() );
 
    jbean.setD(4);
    System.out.println("jbean.d: " + jbean.getD());
 
    jbean.e = 5;
    System.out.println("jbean.e: " + jbean.e);
//    jbean.setE(5);
//    System.out.println(jbean.getE());
 
    System.out.println();
    //===========================================
 
    BeanGroovy gbean = new BeanGroovy();
 
    gbean.setA(1);
    System.out.println("gbean.a: " + gbean.getA());
//    gbean.a = 1;
//    System.out.println(gbean.a);
 
//    gbean.setB(2);
    System.out.println("gbean.b: " + gbean.getB());
 
    gbean.setC(3);
//    System.out.println( gbean.getC() );
 
    gbean.setD(4);
    System.out.println("gbean.d: " + gbean.getD());
 
    gbean.e = 5;
    System.out.println("gbean.e: " + gbean.e);
//    gbean.setE(4);
//    System.out.println(gbean.getE());
  }
}
The point of Groovy is that it makes getting and setting more like reading and assigning; thus, the Java getter call
bean.getProperty()
works in Java and Groovy, but this simplified version is a Groovy equivalent:
bean.property
Likewise, the setter call:
bean.setProperty(value)
works in both Java and Groovy, but this simplified version is a Groovy equivalent:
bean.property = value
What is even more perplexing is that Groovy does not respect the privacy of a data member; therefore we see this very odd behavior which is not mirrored at all in Java:

MainGroovy.groovy
package groovyintro
 
class MainGroovy {
  static void main(args) {    
    BeanJava jbean = new BeanJava()
 
    jbean.a = 1
    println "jbean.a: " + jbean.a
 
    println "jbean.b: " + jbean.b
    jbean.b = 3
    println "jbean.b: " + jbean.b
 
    jbean.c = 5
    println "jbean.c: " + jbean.c
 
    jbean.d = 7
    println "jbean.d: " + jbean.d
 
    jbean.e = 9
    println "jbean.e: " + jbean.e
 
    println ""
 
    //=================================================
    // map-based constructors
 
    def jbean2 = new BeanJava(a:1, b:2, c:3)
    println "$jbean2.a:$jbean2.b:$jbean2.c"
 
    def gbean2 = new BeanGroovy(a:1, b:2, c:3)
    println "$gbean2.a:$gbean2.b:$gbean2.c"
 
    Map params = [a:4, b:5, c:6]
    def gbean3 = new BeanGroovy(params)
    println "$gbean3.a:$gbean3.b:$gbean3.c"
  }
}
The get/set access to the a and d properties are expected, but b is supposed to be get-only and c is supposed to be set-only. The situation is identical for the GroovyBean as well as for the JavaBean depicted in the program.

Object initialization with named parameters

The second part of the demo program illustrates the initialization of both BeanJava and BeanGroovy objects through a constructor in which the data members a, b and c are initialized by referring to their names with associated values.

The very last statement illustrates what is really going on in that the named parameters are constituted into a key/value Map (see later section) which is what is actually passed to the object.

The printed content of the object illustrates yet another feature of Groovy in that it allows insertion of variables in double-quoted strings when the variables are prefaced with the "$" character.

Groovy Closures

The Groovy closure is a first-class function with the external syntax:
{ ... }
By function, we mean that it can be called with parameters like a function; by first-class, we mean that it can be treated like a value, i.e., assigned to a variable or passed as an argument into certain expressions. One of the effects of adding a closure is that it makes Java array literals unusable:
String[] s = {"hello", "world"};  // not OK in Groovy
It is too large a task to detail all the aspects of closures in one sitting, but here is an example program:

MainClosures.groovy
package groovyintro
 
class SampleClass {
  def foo(x) { "foo: " + x }  // method
 
  def bar = { x -> "bar: " + x }  // closure
 
  def foobar = { println "foobar: " + it }  // it = default param
}
 
class MainClosures {
  static void main(args) {
    def sample = new SampleClass()
 
    println sample.foo()
    println sample.foo(1)
//    println sample.foo(1,2)
 
    println sample.bar()
    println sample.bar(1)
//    println sample.bar(1,2)
 
    sample.foobar(7)
    sample.foobar 8   // parens not needed in some circumstances
 
    def func = sample.bar
 
    println func(5)
  }
}
The entities sample.foo and sample.bar are a method and closure, respectively, yet they have many aspects in common. The big difference is that sample.bar is a value in its own right, which can be assigned to the variable func, which then can be used as a function.

Groovy Data Structures

The Java/Groovy Data Structure constructs are loosely called collections. They all are classes and interfaces belonging to the java.util package. By default, Groovy programs automatically imports java.util.*.

Here are some of the standard components:
Iterable (interface)
  Collection (interface)
    List (interface)
      ArrayList (class)
      LinkedList (class)
    Set (interface)
      HashSet (class)
        LinkedHashSet (class)
      NavigableSet (interface)
        TreeSet (class)
Map (interface)
  HashMap (class)
    LinkedHashMap (class)
  NavigableMap (interface)
    TreeMap (class)
Some of the features of this class/interface heirarchy are:
  1. a Collection expresses an "aggregate" of elements to which you want to apply these methods (among others):
    add(element), contains(element), remove(element), size(), isEmpty(), clear(), ...
    
  2. a List expresses the notion of position, and you want the further positional methods:
    get(pos), set(pos), add(pos, element), indexOf(element), ...
    
  3. an ArrayList gives an array-based implementation of a List, and a LinkedList gives a pointer-chain implementation
  4. a Set is a collection which avoids duplicates
  5. a HashSet implements a Set as a hash table, and a LinkedHashSet adds a LinkedList which gives a predictable iteration order based on the order of entry of the elements
  6. a NavigableSet is one which expresses a comparative ordering of elements with additional methods:
    first(), last(), higher(elt), lower(elt), ...
    
  7. a Map expresses a set of key/value pairs in which there are no duplicate keys
  8. a HashMap implements a Map as a hash table by hashing the key, and a LinkedHashMap gives a predictable iteration order based on the entry order
Groovy adds a number of syntactic features on top of Java to enhance the creation and manipulation of collections. In particular, there are collection literals, all based on the "[ ... ]" notation, e.g.:
def list = [11, 22, 33]            // ArrayList
def map = ['john':11, 'jill':33]   // LinkedHashMap
Gimmicks are needed to represent a Set or an empty map:
def empty_list = [] 
def empty_set = [] as Set  // LinkedHashSet
def empty_map = [:]
The following program illustrates various features of Groovy data structures:

MainDataStructures.groovy
package groovyintro
 
class MainDataStructures {
  static void main(args) {
    List numbers = [22, 33, 55]
    Set colors = ['red', 'blue', 'green']
    Map ages = [john: 5, jim: 10, joan: 12]
 
    println "numbers class: " + numbers.getClass()  // ArrayList
    println "colors class:  " + colors.getClass()   // HashSet
    println "ages class:    " + ages.getClass()     // LinkedHashMap
    println ""
 
    println "numbers: "
    numbers.each( { println it } )
    println ""
 
    println "ages: "
    ages.each { println "$it.key:$it.value" } 
    println ""
 
    [ colors, numbers, ages ].each{ println it } 
    println ""
 
    println "numbers[1]   = " + numbers[1]
    println "ages['john'] = " + ages['john']
    println "ages.john    = " + ages.john     // map like an object
    println ""
 
    numbers << 44
    numbers << 33
 
    colors << 'orange'
    colors << 'blue'
 
    ages['john'] = 11
    ages['bill'] = 44
 
    [ colors, numbers, ages ].each { println it }
    println ""
 
    def mylist  = [ 'red', 22, 33.5 ]
    def numbers1 = [22, 33, 55] as LinkedList
    def colors1 = [ 'red', 'blue', 'green' ] as Set
    def ages1 = [] as HashMap
 
    println "mylist class:   " + mylist.getClass()    // ArrayList 
    println "numbers1 class: " + numbers1.getClass()  // LinkedList
    println "colors1 class:  " + colors1.getClass()   // LinkedHashSet
    println "ages1 class:    " + ages1.getClass()     // HashMap
    println ""
 
    colors1 << 'orange'
    colors1 << 'blue'
 
    println "colors:     " + colors
    println "colors1:    " + colors1
    println "colors[3]:  " + colors1[3]   // for HashSet, makes no sense
    println "colors1[3]: " + colors1[3]   // for LinkedHashSet, makes sense
    println ""
 
    print "ages1 entries:"
    ages.each{ key,value ->
      print " ($key,$value)"
      ages1[key] = value 
    }
    println ""
    println "ages1: " + ages1
  }
}
Several further points:
  1. We can iterate through a collection using the each operator, e.g.,
    numbers.each( { println it } )
    
    We are passing a closure into the each member function. It is common in this circumstance and others to drop the parentheses around the closure argument, getting:
    numbers.each { println it }
    
  2. The each operator works for a maps as well. What is hidden is that you are actually iterating through the map's entrySet. In Java this must be made explicit, e.g.:
    for(Map.Entry<String,Integer> entry: ages.entrySet()) {
      // use: entry.getKey(), entry.getValue());
    }
    In Groovy, the entrySet member function is called implicitly, e.g.:
    ages.each { println "$it.key:$it.value" } 
    // or
    ages.each { key,value -> println "$key:$value" }
    
  3. The collection-based add operator can be written with the Groovy << operator, e.g.:
    numbers << 44 
    colors << 'orange'
    
  4. The get operator is represented by the array-like "[..]" access syntax, e.g.
    numbers[2]    // value at position 2 in the list
    ages['john']  // value for key 'john'
    
    What makes less sense is positional access for Set objects; the get operation is not defined for sets in Java. Our program illustrates two examples in the program with colors and colors1. The iteration listing and positional access values generated are these:
    colors:     [orange, red, blue, green]
    colors1:    [red, blue, green, orange]
    colors[3]:  orange
    colors1[3]: orange
    
    Since colors1 is a LinkedHashSet, the positional value makes sense, but not so for colors, which is a HashSet.
  5. A Map is created as a literal with the standard notation
    [ key1:value1, ... ]
    
    will always be created as a LinkedHashMap (which is mostly what the user would want anyway); any added HashMap type declaration will be ignored.

Java Equivalent

In Java the data structure classes are not subject to hidden assumptions, and so the classes must always be explicitly declared. This program gives Java equivalents of the Groovy constructs (for most statements):
MainJavaDataStructures.java  


© Robert M. Kline