CHAPTER 2

Groovy Basics

Chapter 1 introduced you to Groovy, its relationship to Java, and where it differs. This chapter will delve into the Groovy language. The focus will be on language features commonly used to build Grails applications. First, you will learn about Groovy scripts, including compiling and running Groovy scripts using the command line, Groovy Shell, and Groovy Console. Then we will focus on specific aspects of the Groovy language: assertions, strings, methods, closures, collections, ranges, regular expressions, and operators.

Scripts

You will be using the Groovy language to build: domain objects, controllers, and services. But that isn't the only way to use Groovy. In addition to building classes, you can use Groovy as a scripting language.

You will see detailed examples of scripts in Chapter 12, which covers using scripts in an application context to access a web service. But here we'll start with a simple script. Listing 2-1 is an example of a very simple Groovy "Hello" script that takes an argument and uses it to print a message.

Listing 2-1. A Simple Groovy Script, Hello.groovy

println "Hello ${args[0]}, may Groovy be with you."

Execute the script by typing the following on the command line:

>groovy Hello "Luke Skywalker"


Note If you are on Windows environment and installed Groovy with the installer, you can omit the groovy on the command line. By default, the installer is set up to map files with the .groovy file extension to the Groovy runtime.


The script will output the results:


Hello Luke Skywalker, may Groovy be with you.

On execution of the script, Groovy generates a class with the same name as the script source file, including a main method that contains the script source.

The equivalent Java application would look like Listing 2-2.

Listing 2-2. The Java Version, HelloJava.java

package com.apress.beginninggrails.cli.scripts;

public class HelloJava {
    public static void main(String[] args) {
        System.out.println( "Hello "+ args[0], may Java be with you.);
    }
}

Notice how much more verbose the Java version is compared to the Groovy version. With Java, you need to define a class and a main method. You also must fully qualify the println method, add parentheses, and terminate it with a semicolon. Then you need all of the closing curly braces. Even if you are a Java fan, you have to admit that the Groovy example is a good bit shorter and easier to read! Furthermore, you don't need to go through a separate step of compiling Groovy before it is executed.

Using Script Functions

Just like most scripting languages, Groovy scripts can be organized into blocks of reusable code. In scripts, these blocks are called functions. Listing 2-3 is an example of creating and using a function. It creates a simple function to print a name and calls the function with two different names.

Listing 2-3. A Script Function, PrintFullName.groovy

def printFullName(firstName, lastName) {
    println "${firstName} ${lastName}"
}

printFullName('Luke', 'SkyWalker')
printFullName('Darth', 'Vader')

This example defines the printFullName function, which takes two parameters. Next, the function is invoked twice: once to print Luke Skywalker and again to print Darth Vader.

Compiling Groovy

In the previous examples, we let Groovy compile the script on the fly. Like Java, Groovy can be compiled to Java bytecode. Listing 2-4 illustrates compiling the Groovy script in Listing 2-1.

Listing 2-4. Compiling Groovy with groovyc

groovyc Hello.groovy

As you might expect, compiling Hello.groovy results in Hello.class. Because groovyc compiles to Java bytecode, you can use the Java command line to execute it. Listing 2-5 illustrates running the program using Java.

Listing 2-5. Running the Groovy Program Using Java

java -cp %GROOVY_HOME%/embeddable/groovy-all-1.5.4.jar;. Hello "Luke Skywalker"
Hello Luke Skywalker

Being able to run the Groovy program using Java proves it—Groovy is Java. If you look at Listing 2-5, you'll see that the only thing special required to run the Groovy compiler is to include groovy-all-<version>.jar on the classpath.

The Groovy compiler is a joint compiler. It can compile Groovy and Java code at the same time. The joint compiler first became available in Groovy 1.5 through a generous donation by JetBrains, the makers of IntelliJ IDEA. The joint compiler allows you to compile Groovy and Java files with a single compile statement. Listings 2-6 and 2-7 are a Groovy file and a Java file, respectively, to demonstrate joint compilation.

Listing 2-6. A Sample Groovy File, Name.groovy

class Name
{
   String firstName
   String toString() { return "Hello ${firstName}, Java calling Groovy" }
}

Listing 2-7. A Sample Java File, SayHello.java

public class SayHello
{
  public static void main( String args[] )
  {
    Name name = new Name();
    name.setFirstName( args[0] );

    System.out.println( name.toString() );
  }
}

The Java class, SayHello, instantiates the Groovy class Name and sets the firstName property to a value passed in on the command line. Listing 2-8 illustrates compiling and executing the programs.

Listing 2-8. Joint Compile and Execute

groovyc *.groovy *.java

java -cp %GROOVY_HOME%/embeddable/groovy-all-1.5.6.jar;. SayHello "Luke"
Hello Luke, Java calling Groovy

Compiling the Groovy and Java classes is accomplished by telling groovyc to compile files matching the file pattern ending in .groovy and .java. You run the program in the same way that you run any other Java program—just include groovy-all-<version>.jar in the classpath.


Caution If you run groovyc without parameters, you will get the usage information. Looking at the usage information, you might come to the conclusion that you should use the −j switch. Groovy 1.5.0—1.5.5 had a bug1 that would cause a compile error when using the −j switch. The bug was fixed in Groovy 1.5.6. If you are using an older version of Groovy and encounter an error, try not using the −j switch.


Running Groovy

You can run Groovy scripts and classes through the command line, Groovy Shell, or Groovy Console. Let's look at each technique.

_______________

Command Line

To run Groovy from the command line,2 you have two or three options:

  • Use Groovy directly by typing groovy MyPgm at the command line. (If you installed Groovy using the Windows installer, you can omit groovy and just type MyPgm.) If you are running a script, Groovy will generate a class with a main method containing the script commands, compile the script, and execute it. If you don't want to recompile the file each time it is run, you can use the third option.
  • Compile the file using groovyc into a class and execute it using Java. You saw an example of this approach in the previous section.
  • If you're in the Windows environment and Groovy was installed with the Windows Installer with the PATHEXT option, you can omit the leading groovy and just type MyPgm.groovy. The PATHEXT option associates files ending with .groovy to the Groovy runtime. On Unix platforms, you can use a shebang at the top of the file to get the same result:

    #!/usr/bin/groovy
    println "Hello ${args[0]}, may Groovy be with you."

Groovy Shell

The Groovy Shell3 is an interactive command-line application (shell) that allows you to create, run, save, and load Groovy scripts and classes. To start the Groovy Shell, run groovysh. Figure 2-1 illustrates using the Groovy Shell to execute a simple script.

image

Figure 2-1. Using the Groovy Shell

_______________

As you can see, the script prints Hello Luke Skywalker. Then you see ===> null. As a matter of convention, Groovy always returns the results of methods. In this case, there is no result, so null is returned.

The Groovy Shell contains a built-in help facility that you can use to learn more about the shell. To access it, type help at the prompt. Figure 2-2 shows the help listing.

image

Figure 2-2. Groovy Shell help information

Groovy Console

The Groovy Console,4 shown in Figure 2-3, is a graphical version of the Groovy Shell. It is written using SwingBuilder, a Groovy module that makes building Swing user interfaces easier.

image

Figure 2-3. Groovy Console

_______________

You can start the Groovy Console in a number of ways, depending on your environment and how you installed Groovy. The easiest way is to execute groovyConsole, which is located in the Groovy bin directory.

The console provides the ability to create, save, load, and execute classes and scripts. Some of the nice features of the console are undo/redo and the ability to inspect variables.

If you have to choose between using the Groovy Shell and the Groovy Console, we recommend the Groovy Console. To a beginner, the Groovy Shell behavior can seem to be a bit unpredictable. For example, try the code from Listing 2-9 in the shell and then in the console.

Listing 2-9. Shell/Console Experiment

name = "Luke Skywalker"
def name = "Darth Vader"
println name

Running this code from the shell results in Luke Skywalker being printed. Running the code from the console results in Darth Vader being printed. The reason for the difference can be found in the Groovy Shell documentation.5 The first instance of name causes a shell variable to be created and assigned the value Luke Skywalker. The second instance of name (def name) causes a local variable to be created and assigned the value Darth Vader. The shell executes expressions as soon as it sees a complete expression. In the case of the second instance of name, it was a complete expression that was executed and immediately went out of scope. When the final line of code (println) is executed, the only currently accessible name variable is assigned the value Luke Skywalker.

Assertions

As a developer, if you have used JUnit6 (or any of the flavors of JUnit), you already have some idea what an assertion is. An assertion is used to validate that an expected condition is true. If the expected condition is not true, a java.lang.AssertionError 7 is thrown. You test that the expected condition is true by using Groovy expressions.

Taking advantage of Groovy's truth8 required Groovy developers to create their own version of assert. They could not leverage the Java version of assert9 because it is restricted to Java's version of truth. You will also notice that the syntax is the same. Listing 2-10 illustrates the Java and Groovy versions of assert.

_______________

Listing 2-10. Java and Groovy Assertions

// Java assert
assert 1==2 : "One isn't Two";

// Groovy assert
assert 1==2 : "One isn't Two"

As you can see, the Groovy assert syntax is the same as Java's, except for the ending semicolon. The message is to the right of the expression and separated by a colon. As with Java, the message portion of the assert is optional.


Tip As a best practice, when you are using assertions, you should include a message. It will help the next person maintaining your code to understand its intent.


When an assertion fails, Groovy throws a java.lang.AssertionError. Listing 2-11 is an example of the Groovy assertion in Listing 2-10 failing.

Listing 2-11. Sample Assertion Failure

ERROR java.lang.AssertionError: One isn't Two Expression: (1 == 2)

As you can see, the error message from Listing 2-10 is embedded in Listing 2-11.

Assertions are very handy and one of the cornerstones of good testing. They also do a great job of clarifying intentions. You will see assertions in many of the examples throughout this book.

Strings

Like most modern languages, Groovy has the concept of a string. In Groovy, a string can be defined three different ways: using double quotes, single quotes, or slashes (called "slashy strings"). Listing 2-12 illustrates the three different ways to define a string.

Listing 2-12. Groovy String Definition

01 // Quote
02 def helloChris = "Hello, Chris"
03 println helloChris.class.name // java.lang.String
04
05 // Single quote
06 def helloJoseph = 'Hello, Joseph'
07 println helloJoseph.class.name // java.lang.String
08
09 // Slashy string
10 def helloJim = /Hello, Jim/
11 println helloJim.class.name // java.lang.String

Just to prove that the variables are normal java.lang.String strings, run the code in Listing 2-12. The output should look like this:


java.lang.String
java.lang.String
java.lang.String

Groovy also supports a more advanced string called a GString. A GString is just like a normal string, except that it evaluates expressions that are embedded within the string, in the form ${...}. When Groovy sees a string defined with double quotes or slashes and an embedded expression, Groovy constructs an org.codehaus.groovy.runtime.GStringImpl instead of a java.lang.String. When the GString is accessed, the expression is evaluated. Listing 2-13 illustrates using a GString and embedded expressions.

Listing 2-13. GString and Embedded Expressions

01 def name = "Jim"
02 def helloName = "Hello, ${name}"
03 println helloName // Hello, Jim
04 println helloName.class.name // org.codehaus.groovy.runtime.GStringImpl
05
06 def helloNoName = 'Hello, ${name}'
07 println helloNoName // Hello, ${name}
08 println helloNoName.class.name // java.lang.String
09
10 def helloSlashyName = /Hello, ${name}/
11 println helloSlashyName // Hello, Jim
12 println helloSlashyName.class.name // org.codehaus.groovy.runtime.GStringImpl

Run the code in Listing 2-13 to see the expression evaluation and class names:


Hello, Jim
org.codehaus.groovy.runtime.GStringImpl
Hello, ${name}
java.lang.String
Hello Jim
org.codehaus.groovy.runtime.GStringImpl

Let's take a look at Listing 2-13 in a little more detail:

  • Line 1 defines a variable, name, and assigns the value "Jim".
  • Line 2 defines a GString, helloName, and assigns it to "Hello" plus the expression ${name}.
  • Line 3 prints the GString. Accessing the GString causes the expression to be evaluated and results in Hello, Jim.
  • Line 4 prints out helloName's class name to prove that it is a GString.
  • Lines 6–8 take the same approach but define the string with single quotes. The result is a regular Java string, and the expression is not evaluated.
  • Lines 10–12 take the same approach but define the string using slashes. The result is a GString, just as in the first example. When the string is printed, the expression is evaluated, and Hello, Jim is printed.

The evaluation of expressions within strings is called interpolation, as discussed next.

String Interpolation

String interpolation is the ability to substitute an expression or variable within a string. If you have experience with Unix shell scripts, Ruby, or Perl, this should look familiar. If you look closely at Listing 2-13, you can see string interpolation in action. Strings defined using double quotes and slashes will evaluate embedded expressions within the string (see lines 2 and 10 of Listing 2-13). Strings defined with single quotes don't evaluate the embedded expressions (see line 6 of Listing 2-13).

Java doesn't support string interpolation. You must manually concatenate the values together. Listing 2-14 is an example of the type of code you need to write in Java.

Listing 2-14. Building Strings with Java

String name = "Jim";
String helloName = "Hello " + name;
System.out.println(helloName);

While this is an extremely simple example, imagine what it might look like if you were building some XML or a SQL statement. It gets very difficult to read very quickly.

String interpolation is a nice feature and is used in the examples throughout this book to build up strings.

Multiline Strings

Groovy supports strings that span multiple lines. A multiline string is defined by using three double quotes or three single quotes.

Multiline string support is very useful for creating templates or embedded documents (such as XML templates, SQL statements, HTML, and so on). For example, you could use a multiline string and string interpolation to build the body of an e-mail message, as shown in Listing 2-15. String interpolation with multiline strings works in the same way as it does with regular strings: multiline strings created with double quotes evaluate expressions, and single-quoted strings don't.

Listing 2-15. Using Multiline Strings

def name = "Jim"
def multiLineQuote = """
Hello, ${name}

This is a multiline string with double quotes
"""

println multiLineQuote
println multiLineQuote.class.name

def multiLineSingleQuote = '''
Hello, ${name}

This is a multiline string with single quotes
'''

println multiLineSingleQuote
println multiLineSingleQuote.class.name

Running the code in Listing 2-15 results in the following output:


Hello, Jim

This is a multiline string with double quotes
org.codehaus.groovy.runtime.GStringImpl

Hello, ${name}

This is a multiline string with single quotes
java.lang.String

Slashy Strings

As mentioned earlier, slashes can be used to define strings. The slashy notation has a very nice benefit: additional backslashes are not needed to escape special characters. The only exception is escaping a backslash: /. The slashy notation can be helpful when creating a regular expression requiring a backslash or a path. Listing 2-16 illustrates the difference between using regular quotes and slashes to define a regular expression to match a file system path.

Listing 2-16. Using Slashy Strings

def winpathQuoted='C:\windows\system32'
def winpathSlashy=/C:windowssystem32/
println winpathSlashy  // C:windowssystem32

assert winpathSlashy ==~ '\w{1}:\\.+\\.+'
assert winpathSlashy ==~ /w{1}:\.+\.+/

Listing 2-16 defines two variables and assigns them to a directory path. The first variable definition, winpathQuoted, uses the single-quote notation to define a string. Using the single-quote notation requires that the embedded backslash be escaped using an additional backslash. The first assert statement, which tests the regular expression defined using single quotes, also requires the addition of an extra backslash to escape a backslash. Notice how using the slashy notation doesn't require the additional backslashes.

Clearly, it is easier to write and read winpathSlashy, and the second regular expression is easer to write and read as well. Regular expressions and the ==~ operator will be covered in more detail in the "Regular Expressions" section later in this chapter.

Methods and Closures

You can define a block of reusable code in Groovy in two ways: as a method, as in Java, and as a closure.

Methods

Listing 2-17 illustrates defining a method in Groovy the Java way.

Listing 2-17. Defining a Method the Java Way

public String hello(String name) {
   return "Hello, " + name;
}

Listing 2-18 illustrates defining the method using the Groovy idiom.

Listing 2-18. Defining a Method Using the Groovy Idiom

def hello(name) {
    "Hello, ${name}"
}

The Groovy way of defining is method is a bit more compact. It takes advantage of a couple of Groovy's optional features:

  • The return type and the return statement are not included in the body of the method. Groovy always returns the results of the last expression—in this case, the GString "Hello, . . . ".
  • The access modifier public is not defined. By default, unless you specify otherwise, Groovy defaults all classes, properties, and methods to public access.

Note Strictly speaking, the Groovy version of the hello method (Listing 2-18) is not exactly like the Java version (Listing 2-17). The corresponding Java signature of the method would be: public Object hello(Object name) But, functionally, they are very close to being the same.


Closures

A Groovy closure is a block of reusable code within curly braces {}, which can be assigned to a property or a variable, or passed as a parameter to a method.10 The code within the curly braces is executed when the closure is invoked. In this form, the closure functions just like a Java method. The difference between methods and closures is that a closure is an object, and a method isn't. You will see why this is valuable in just a few moments. Listing 2-19 is an example of defining and invoking a closure.

Listing 2-19. Using a Closure

def name = "Chris"
def printClosure = { println "Hello, ${name}" }

printClosure()

name = "Joseph"
printClosure()


Hello, Chris
Hello, Joseph

This example demonstrates that just like methods, closures can access variables defined in the same scope as the closure.

And just as with methods, parameters can be passed to closures as well. Listing 2-20 shows an example of passing parameters to closures.

Listing 2-20. Passing Parameters to Closures

def printClosure = {name -> println "Hello, ${name}" }

printClosure("Chris")
printClosure("Joseph")
printClosure "Jim"

_______________

.
Hello, Chris
Hello, Joseph
Hello, Jim

In this example, printClosure takes a name parameter. Compare Listing 2-19 to Listing 2-20. Listing 2-19 is an example of closure accessing the name variable, and Listing 2-20 is an example of a closure taking a name parameter.

The third invocation of printClosure in Listing 2-20 does not include parentheses. This is not a typo. This is another one of the optional parts of Groovy. The fact that there is a parameter after the closure name helps Groovy infer that you want the closure invoked. This works only when a parameter is involved. In Listing 2-19, where there are no parameters, the parentheses are required.

Multiple parameters can be passed as well. Listing 2-21 illustrates calling a closure with multiple parameters.

Listing 2-21. Passing Multiple Parameters to a Closure

def printClosure = {name1, name2, name3 -> println "Hello, ${name1},
    ${name2}, ${name3}" }

printClosure "Chris", "Joseph", "and Jim"


Hello, Chris, Joseph, and Jim

In this example, the "Hello, . . ." and expressions were evaluated when the closure was invoked.

An advanced usage of closures is to bind the closure to values at the time it is defined. Listing 2-22 is an example of using this technique to create a timer.

Listing 2-22. Binding Values to Closures

01 def startTimer() {
02   def initialDate = new java.util.Date()
03   return { println "${initialDate} - ${new java.util.Date()} : Elapsed time
                    ${System.currentTimeMillis() - initialDate.time}" }
04 }
05
06 def timer = startTimer()
07 // Simulate some work
08 sleep 30000
09 timer()
10 // Simulate some more work
11 sleep 30000
12 timer()
13
14 // Reset the timer
15 println "Reset the Timer"
16 timer = startTimer()
17 timer()
18 sleep 30000
19 timer()


Sat Mar 01 09:29:27 EST 2008 - Sat Mar 01 09:29:57 EST 2008 : Elapsed time 29998
Sat Mar 01 09:29:27 EST 2008 - Sat Mar 01 09:30:27 EST 2008 : Elapsed time 59997
Reset the timer
Sat Mar 01 09:30:27 EST 2008 - Sat Mar 01 09:30:27 EST 2008 : Elapsed time 0
Sat Mar 01 09:30:27 EST 2008 - Sat Mar 01 09:30:57 EST 2008 : Elapsed time 29999

In Listing 2-22, lines 1–4 define a method, startTimer(), that returns a closure. The value of the variable initialDate, a java.util.Date object, is bound to the closure at the time it is defined, in line 6. In lines 7–12, when the closure is invoked, the expressions are evaluated. Line 16 invokes the startTimer() method, which causes the closure to be redefined and the results in the timer to be reset.

A closure is an object. You can pass closures around just like any other objects. A common example is iterating over a collection using a closure. Listing 2-23 illustrates passing a closure as a parameter.

Listing 2-23. Passing a Closure As a Parameter

def list = ["Chris", "Joseph", "Jim"]
def sayHello = { println it }
list.each(sayHello)

Notice that sayHello is a property whose value is a closure. It is passed to the each() method so that as each() iterates over the list, the sayHello closure is invoked.

Collections

Groovy supports a number of different collections, including lists, ranges, sets, arrays, and maps. Let's look at how to create and use each of the collection types.

Lists

A Groovy list 11 is an ordered collection of objects, just as in Java. It is an implementation of the java.util.List 12 interface. In the course of building Grails applications, it is common to see lists returned from the controllers and services. Listing 2-24 illustrates creating a list and common usages.

Listing 2-24. Creating and Using Lists

01 def emptyList = []
02 println emptyList.class.name // java.util.ArrayList
03 println emptyList.size // 0
04
05 def list = ["Chris"]  // List with one item in it
06 // Add items to the list
07 list.add "Joseph"  // Notice the optional () missing
08 list << "Jim"  // Notice the overloaded left-shift operator
09 println list.size // 3
10
11 // Iterate over the list
12 list.each { println it } // Chris Joseph Jim
13
14 // Access items in the list
15 println list[1]  // Joseph // Indexed access
16 list[0] = "Christopher"
17 println list.get(0)  // Christopher
18
19 list.set(0, "Chris") // Set the 0 item to Chris
20 println list.get(0)  // Chris
21
22 list.remove 2
23 list-= "Joseph"  // Overloaded - operator
24 list.each { println it } // Chris
25
26 list.add "Joseph"
27 list+="Jim" // Overloaded + operator
28 list.each { println it } // Chris Joseph Jim
29 println list[-1]  // Jim

_______________

On line 1 of Listing 2-24, an empty list is created by assigning a property the value of []. Line 2 prints out the list's class name so that you can see that it is a java.util.ArrayList. Line 3 prints the list's size, which is 0.

Lines 5–9 create a list with an item already in it and show two ways to add items to the list. Line 12 iterates over the list, invoking the closure to print out the contents. The each() method provides the ability to iterate over all elements in the list, invoking the closure on each element. This is an example of using a closure as a parameter to a method.

Lines 15–17 illustrate using an index to access a list. Lists are zero-based. Line 15 shows accessing the second item in the list. Line 16 shows using an index to assign position 0 the value "Christopher". Line 17 accesses the list using the get() method. Lines 19–20 use the set() method to assign the first position in the list and then print it out.

Lines 22–24 remove items from the list using the remove() method and the minus operator. Lines 26–28 add items to the list using the add() method and the plus operator.

Line 29 is interesting—it uses the index value −1. Using a negative index value causes the list to be accessed in the opposite order, or from last to first.

Ranges

A range is a list of sequential values. Logically, you can think of it as 1 through 10 or a through z. As a matter of fact, the declaration of a range is exactly that: 1..10, or 'a'..'z'.

A range is a list of any objects that implements java.lang.Comparable.13 The objects have next() and previous() methods to facilitate navigating through the range. This means that with a bit of work, it is possible to use your own Groovy objects within a range. Listing 2-25 illustrates some of things you can do with ranges.

Listing 2-25. Creating and Using Ranges

01 def numRange = 0..9
02 println numRange.size() // 10
03 numRange.each {print it}  // 0123456789
04 println ""
05 println numRange.contains(5)  // true
06
07 def alphaRange = 'a'..'z'
08 println alphaRange.size()  // 26
09 println alphaRange[1]    // b
10
11 def exclusiveRange = 1..<10
12 println exclusiveRange.size() // 9
13 exclusiveRange.each {print it}  // 123456789
14 println ""
15 println exclusiveRange.contains(10)  // false
16
17 def reverseRange = 9..0
18 reverseRange.each {print it} // 9876543210

_______________

Lines 1, 7, 11, and 17 illustrate defining ranges. Line 1 defines an inclusive range of numbers. Line 7 defines an inclusive range of lowercase letters. Line 11 defines an exclusive list of numbers. The range results in a range of numbers 1–9, excluding 10. Line 17 creates a range in reverse order, 9 through 0.

Frequently, ranges are used for iterating. In Listing 2-25, each() was used to iterate over the range. Listing 2-26 shows three ways you could use a range to iterate: one Java and two Groovy.

Listing 2-26. Iterating with Ranges

01 println "Java style for loop"
02 for(int i=0;i<=9;i++) {
03    println i
04 }
05
06 println "Groovy style for loop"
07 for (i in 0..9) {
08    println i
09 }
10
11 println "Groovy range loop"
12 (0..9).each { i->
13    println i
14 }

Listing 2-26 starts off by showing a classic Java style for loop. Lines 7–9 are an example of the same loop using the Groovy style for loop. Lines 12–14 illustrate yet another technique for looping, by using a range and the each() method.

Sets

A Groovy set14 is an unordered collection of objects, with no duplicates, just as in Java. It is an implementation of java.util.Set.15 By default, unless you specify otherwise, a Groovy set is a java.util.HashSet.16 If you need a set other than a HashSet, you can create any type of set by instantiating it; for example, def aTreeSet = new TreeSet(). In general, we encourage you just to think of it as a regular set. Listing 2-27 illustrates creating sets and common usages.

Listing 2-27. Creating and Using Sets

01 def emptySet = [] as Set
02 println emptySet.class.name //  java.util.HashSet
03 println emptySet.size() // 0
04
05 def list = ["Chris", "Chris" ]
06 def set = ["Chris", "Chris" ] as Set
07 println "List Size: ${list.size()} Set Size: ${set.size()}" // List Size: 2
Set Size: 1
08 set.add "Joseph"
09 set << "Jim"
10 println set.size() // 3
11 println set  // ["Chris", "Jim", "Joseph"]
12
13 // Iterate over the set
14 set.each { println it }
15 S
16 set.remove 2
17 set-= "Joseph"  // Overloaded - operator
18 set.each { println it } // Chris
19 set+= "Joseph"
20 set+= "Jim"
21 set.each { println it } // Chris Joseph Jim
22
23 // Convert a set to a list
24 List = set as List
25 println list.class.name // java.util.ArrayList
26 println set.asList().class.name // java.util.ArrayList
27 println set.toList().class.name // java.util.ArrayList

_______________

Creating an empty set is similar to creating an empty list. The difference is the addition of the as Set clause. Lines 5–7 illustrate that a list allows duplicates and a set doesn't. Lines 8–21 shouldn't be any surprise. One of the important differences between a list and a set is that a list provides indexed-based access and a set doesn't. Lines 24 and 26 show two different techniques to convert a Set into a List.

Arrays

A Groovy array17 is a sequence of objects, just like a Java array.18 Groovy makes working with arrays a little easier, but you have the same limitations as with Java arrays. Listing 2-28 illustrates creating and using arrays.

Listing 2-28. Creating and Using Arrays

01 def stringArray = new String[3]
02 println stringArray.size()
03 stringArray[0] = "Chris"
04 println stringArray  // {"Chris", null, null}
05 stringArray[1] = "Joseph"
06 stringArray[2] = "Jim"
07 println stringArray // {"Chris", "Joseph", "Jim"}
08 println stringArray[1] // Joseph
09 stringArray.each { println it} // Chris, Joseph, Jim
10 println stringArray[-1..-3] // ["Jim", "Joseph", "Chris"]

Line 1 creates a string array of size 3. Lines 3–8 use an index to access the array. Line 9 illustrates using the each() method to iterate through the array. That deserves a second look. Yes, the each() method is available on the array, which is very convenient. Line 10 also shows something interesting—it uses a range to access the array. In this case, the example goes one step further and shows accessing the array from left to right.

_______________

Maps

A Groovy map19 is an unordered collection of key/value pairs, where the key is unique, just as in Java. It is an implementation of java.util.Map.20 By default, unless you specify otherwise, a Groovy map is a java.util.LinkedHashMap.21 If you are familiar with LinkedHashMap maps, you know that they are ordered by insert.

If you need a map other than a LinkedHashMap, you can create any type of map by instantiating it; for example, def aTreeMap = new TreeMap(). In general, we encourage you just to think of it as a regular map.

Listing 2-29 illustrates creating maps and common usages.

Listing 2-29. Creating and Using Maps

01 def emptyMap = [:]
02 // map.class returns null, use getClass()
03 println emptyMap.getClass().name  //java.util.LinkedHashMap
04 println emptyMap.size() // 0
05
06 def todos = ['a':'Write the map section', 'b':'Write the set section']
07 println todos.size() // 2
08 println todos["a"] // Write the map section
09 println todos."a"  // Write the map section
10 println todos.a    // Write the map section
11 println todos.getAt("b") // Write the set section
12 println todos.get("b")   // Write the set section
13 println todos.get("c", "unknown") //  unknown, Notice "c" wasn't defined
14                                   // and now it is
15 println todos // ["a":"Write the map section", "b":"Write the set section",
16               // "c":"unknown"]
17
18 todos.d = "Write the ranges section"
19 println todos.d // Write the ranges section
20 todos.put('e', 'Write the strings section')
21 println todos.e // Write the strings section
22 todos.putAt 'f', 'Write the closure section' // Notice () are optional
23 println todos.f // Write the closure section
24 todos[null] = 'Nothing Set'  // Using null as a key
25 println todos[null] // Nothing set
26
27 // Print each key/value pair on a separate line
28 // Note: it is an implicit iterator
29 todos.each { println "Key: ${it.key}, Value: ${it.value}" }
30 // Print each key/value pair on a separate line with index
31 todos.eachWithIndex { it, i -> println "${i} Key: ${it.key},
32     Value: ${it.value}" }
33 // Print the value set
34 todos.values().each { println it }

_______________

In line 1, an empty map is created by assigning a property the value [:]. Compare the creation of an empty list to the creation of an empty map. An empty list is created using the value []; an empty map is created using the value [:]. You can see from line 2 that the map is implemented as a java.util.LinkedHashMap.

Line 6 illustrates defining a map with multiple entries. When using the square bracket notation, the colon separates the key from the value. Line 6 is [ key1: value1, key2 : value2 ].

Lines 8–16 show several different techniques for accessing the map. The most interesting is line 10. It shows using the key as a property to the map. You can use this technique to read an item from the map and put an item into the map.

Lines 18–25 show several different techniques for putting items into the map. You can see that they mirror the techniques used on lines 8–16.

Lines 29, 31, 32, and 34 illustrate iterating. Line 29 iterates over the map to print the key and value. Lines 31 and 32 iterate with an index. Line 34 iterates over the map values.

Regular Expressions

Regular expressions, sometimes referred to regex, are a technique for identifying and manipulating text using a pattern notation.22 They have been popular in scripting languages such as Unix shell scripting and Perl for a long time, and were added to Java in version 1.4.23


Note Regular expressions are extremely robust and powerful. This section discusses Groovy's support of regular expressions. For a full exploration of regular expressions, refer to a book devoted to that subject. You can also find many useful tutorials on the Internet.24


_______________

A regular expression is a sequence of characters to create a pattern that is applied to a string. The pattern is defined by a pattern language. Table 2-1 shows some of the more common patterns in the Java regular expression language.25

Table 2-1. Summary of Regular-Expression Constructs

Construct Matches
Characters
x The character x
\ The backslash character
The tab character (u0009)
The newline (line feed) character (u000A)
The carriage-return character (u000D)
f The form-feed character (u000C)
e The escape character (u001B)
Character Classes
[abc] a, b, or c (simple class)
[^abc] Any character except a, b, or c (nagation)
[a-zA-Z] a through z or A through Z, inclusive (range)
[a-d[m-p]] a through d, or m through p: [a-dm-p] (union)
[a-z&&[def]] d, e, or f (intersection)
[a-z&&[^bc]] a through z, except for b and c: [ad-z] (subtraction)
[a-z&&[^m-p]] a through z, and not m through p: [a-lq-z] (subtraction)
Predefined Character Classes
. Any character (may or may not match line terminators)
d A digit: [0–9]
D A nondigit: [^0–9]
s A whitespace character: [ x0Bf ]
S A non-whitespace character: [^s]
w A word character: [a-zA-Z_0-9]
W A nonword character: [^w]
Boundary Matchers
^ The beginning of a line
$ The end of a line
 A word boundary
B A nonword boundary
A The beginning of the input
G The end of the previous match
 The end of the input but for the final terminator, if any
z The end of the input
Greedy Quantifiers
X? X, once or not at all
X* X, zero or more times
X+ X, one or more times
X{n} X, exactly n times
X{n,} X, at least n times
X{n,m} X, at least n but not more than m times
Reluctant Quantifiers
X?? X, once or not at all
X*? X, zero or more times
X+? X, one or more times
X{n}? X, exactly n times
X{n,}? X, at least n times
X{n,m}? X, at least n but not more than m times
Possessive Quantifiers
X?+ X, once or not at all
X*+ X, zero or more times
X++ X, one or more times
X{n}+ X, exactly n times
X{n,}+ X, at least n times
X{n,m}+ X, at least n but not more than m times
Logical Operators
XY X followed by Y
X|Y Either X or Y
(X) X, as a capturing group

_______________

25. For a complete list of regular expressions, see http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html.

Groovy Regular Expression Operators

Groovy leverages Java's regular expression support and makes it easier through Groovy's string support. Groovy also adds three convenience operators:

  • The match operator (==˜)
  • The find operator ()
  • The pattern operator (˜string)

Match Operator

The match operator (==˜) returns true if the regular expression exactly matches the subject. Listing 2-30 shows some examples of using the match operator.

Listing 2-30. Using the Match Operator

01 assert "abc" ==˜ 'abc'
02 assert "abc" ==˜ /abc/
03 assert "abcabc ==˜ /abc/ // Fails – not an exact match
04 assert "abc" ==˜ /^a.c/  // Starts with a, 1 char, ends with c
05 assert "abc" ==˜ /^a../  // Starts with a, 2 chars
06 assert "abc" ==˜ /.*c$/  // One or more chars end with c
07 assert "abc" ==˜ ".*c$" // Slashy string is better

Line 3 shows that unless it is an exact match, the match will fail (return false ). Lines 4–6 illustrate a couple of ways of defining a regular expression that matches the subject. Line 7 is another example of defining the regular expression on line 6, except it uses double quotes instead of slashes. The important thing to note is that using the double quotes requires the $ to be escaped using a (==˜) backslash.

Find Operator

The find operator () returns a java.util.regex.Matcher.26 A matcher is a component that applies a regular expression to a string. The result is a two-dimensional array of matches. The first dimension contains the match, and the second dimension contains the groups within the match. A group is the defined within the regular expression using parentheses. In the example in Listing 2-31, the regular expression defines four groups. When the expression is applied to a string, the groups are individually accessible. This is useful for identifying and accessing portions of a string.

_______________

Listing 2-31. Using the Find Operator

01 def winpath=/C:windowssystem32somedir/
02 def matcher = winpath =˜ /(w{1}):\(w+)\(w+)\(w+)/
03 println matcher
04 println matcher[0] // ["C:windowssystem32somedir", "C", "windows",
05                    //  "system32", "somedir"]
06 println matcher[0][1] // C
07 def newPath = matcher.replaceFirst('/etc/bin/')
08 println newPath // /etc/bin


java.util.regex.Matcher[pattern=(w{1}):\(w+)\(w+)\(w+) region=0,27
 lastmatch=]
["C:windowssystem32somedir", "C", "windows", "system32", "somedir"]
C
/etc/bin/

Line 1 defines winpath as a directory path string. Line 2 applies a regular expression to the winpath variable using the find operator and returns a java.util.regex.Matcher. The regular expression is set up as a series of group-capturing expressions. Referring to Table 2-1, you can see that w is a word character and the + means one or more. When the regular expression matches the variable, the individual group values are available from the matcher. When line 3 is invoked, you can see that matcher is in fact a java.util.regex.Matcher and the pattern is matching. Lines 4 and 5 illustrate printing the contents of the first match using an index notation. In this example, there will be only one match. If winpath had been a multiline string with multiple directory paths, then matcher would have had multiple matches. Line 6 shows accessing the first group within the match using a second index.

Now that you have a match, you can start applying java.util.regex.Matcher methods. Line 7 is an example of replacing the first match with a new value. When you replace a match, it returns a new string.

Pattern Operator

The pattern operator (˜string) transforms a string into a pattern (java.util.regex.Pattern27), which is a compiled regular expression. When you are going to use a regular expression over and over, you should consider creating a pattern. Reusing a pattern will give the application a performance boost.28 Listing 2-32 illustrates creating and using a pattern.

_______________

28. Section 3.5.4, Groovy in Action by Dierk Koenig et al. (Manning, 2007).

Listing 2-32. Creating and Using a Pattern

01 def saying = """Now is the time for all good men (and women) to come to the aid
02 of their country"""
03 def pattern = ˜/(w+en)/
04 def matcher = pattern.matcher(saying)
05 def count = matcher.getCount()
06 println "Matches = ${count}"
07 for(i in 0..<count) {
08   println matcher[i]
09 }


Matches = 2
["men", "men"]
["women", "women"]

Lines 1 and 2 assign a famous quote to the saying variable. Line 3 defines a regular expression pattern that should find the words that end in en, such as men and women.


Caution Notice the space between the = and ˜ for the pattern operator in Listing 2-32. Without the space, it would be the find operator.


Line 4 applies the pattern to the saying and returns a matcher that contains the results. Lines 5 and 6 print the number of matches. Lines 7–9 loop through and print the matches.

Common Uses of Regular Expressions

By now, you are starting to get an idea of how regular expressions work. Now let's see how they can be applied in real-world scenarios. It is common for web applications to allow the user to enter personal information such as a telephone number. Two common tasks are to check that the phone number is a valid format and to parse the phone number into the individual parts. Listing 2-33 is an example of using the match operator to validate a phone number.

Listing 2-33. Validating a Phone Number

def phoneValidation = /^[01]?s*[(.-]?(d{3})[).-]?s*(d{3})[.-](d{4})$/
assert '(800)555-1212' ==˜ phoneValidation
assert '1(800) 555-1212' ==˜ phoneValidation
assert '1-800-555-1212' ==˜ phoneValidation
assert '1.800.555.1212' ==˜ phoneValidation

In this example, you see the same phone number in four different valid formats. The regular expression to validate the phone number format is assigned to the variable phoneValidation. The match operator is used to validate the phone numbers by applying the regular expression to the phone number. If the phone number is valid, the match operator returns true.

Another common task is parsing a phone number so that the values can be put into a domain class. Listing 2-34 illustrates parsing the phone number into the individual parts and loading it in the domain class.

Listing 2-34. Parsing the Phone Number

class Phone {
    String areaCode
    String exchange
    String local
}

def phoneStr = '(800)555-1212'
def phoneRegex = ˜/^[01]?s*[(.-]?(d{3})[).-]?s*(d{3})[.-](d{4})$/
def matcher = phonePattern.matcher(phoneStr)

def phone = new Phone(
    areaCode: matcher[0][1],
    exchange: matcher[0][2],
    local: matcher[0][3])

println "Original Phone Number: ${phoneStr}"
println """Parsed Phone Number
         Area Code = ${phone.areaCode}
         Exchange  = ${phone.exchange}
         Local     = ${phone.local}"""

In this example, the Phone object, the phone number to parse (phone1), and the phone regular expression pattern (phoneRegex) are defined. Next, the phone pattern is applied to the phone number to be parsed. The pattern is defined with groups, which allows the regular expression to be used to parse the phone number into the individual parts. The construction of the Phone object illustrates accessing the individual parts using the second index. Lastly, we print out the phone number to prove that the parsing worked.

Operators

You use operators every day. They probably have become so familiar to you that you don't even think of them anymore. Common operators include = for assignment, + to add two numbers, * to multiply two numbers, and ++ to increment a number. Of course, there are many more, but you get the idea.

Operator Overloading

Operator overloading has been around for some time but absent from Java. Operator overloading was omitted from Java because of the bad experiences people had in C++. Groovy embraces operator overloading and makes it easy to define and use. An overloaded operator executes a method on object. Groovy has predefined the relationship between the overloaded operator and the object method. Table 2-2 lists the Groovy operators and their corresponding methods. When an overloaded operator is encountered, the corresponding method is invoked.

Table 2-2. Operator Overloading

Operator Method
a + b a.plus(b)
a − b a.minus(b)
a * b a.multiply(b)
a ** b a.power(b)
a / b a.div(b)
a % b a.mod(b)
a | b a.or(b)
a & b a.and(b)
a ^ b a.xor(b)
a++ or ++a a.next()
a−− or −−a a.previous()
a[b] a.getAt(b)
a[b] = c a.putAt(b, c)
a << b a.leftShift(b)
a >> b a.rightShift(b)
switch(a){ case(b) : } b.isCase(a)
˜a a.bitwiseNegate()
−a a.negative()
+a a.positive()

At first glance, you may not see the benefits of operator overloading. Groovy uses operator overloading to create shortcuts that make Java friendlier. For example, adding an object to a list in Java looks like this: myList.add(someObject). The corresponding Groovy way of adding an object to a list is myList << someObject.

Operator overloading isn't limited to the predefined instances that Groovy supplies; you can add operator overloading to your Groovy classes by implementing the corresponding method.

Specialized Operators

Groovy includes many of the standard operators found in other programming languages, as well as operators that are specific to Groovy that enable it to be so powerful.

Spread Operator

The spread operator (*.) is a shorthand technique for invoking a method or closure on a collection of objects. Listing 2-35 illustrates two ways of iterating over a list: first using the collect() method and then using the spread operator.

Listing 2-35. Using the Spread Operator

class User {
   String firstName
   String lastName

   def printFullName = {
      println "${firstName} ${lastName}"
   }
}

// Instantiate a User using the named parameters constructor
User chris = new User(firstName:"Chris", lastName: "Judd")
User joseph = new User(firstName:"Joseph", lastName: "Nusairat")
User jim = new User(firstName:"Jim", lastName: "Shingler")

def list = [chris,joseph,jim]

println "Using collect closure"
list.collect { println it.printFullName() }

println " Using Spread Operator:"
list*.printFullName()

This example shows creating a list of User objects and using two different techniques for printing the list. The first technique is using the collect() method to iterate over the list, applying the items' printFullName() closure. The second technique uses the spread operator to iterate over the list of users, invoking the users' printFullName() closure.


Note Listing 2-35 includes a technique that we haven't discussed yet: named parameters. One of the neat things about Groovy is the ability to specify the property and value to be assigned as a parameter to a constructor. Using the code in Listing 2-35 as an example, we can instantiate a User object and set the firstName and lastName, just the lastName, or just the firstName without coding all of the different constructors that would be required.


Elvis Operator

The Elvis operator (?:) is a shorthand version of the Java ternary operator.29 An example of using the Java-style ternary operator is a == 1 ? "One" : "Not One". If a is equal to 1, then "One" is returned; otherwise "Not One" is returned. It is literally a shorthand "if-then-else." As with most Java constructs, you can use the Java ternary operator in Groovy. In addition to the Java ternary operator, you can use an even shorter shorthand notation in Java: the Elvis operator. This can be very useful in defaulting values if they haven't been set already, meaning that they evaluate to null or false. Listing 2-36 illustrates using the Java ternary and Elvis operators in Groovy.

Listing 2-36. Using the Elvis Operator

def firstName = user.firstName == null ? "unknown" : user.firstName // Java ternary
def firstName2 = user.firstName ?: "unknown" // Groovy Elvis

In both cases, if the user.firstName is null, then the firstName is set to "unknown". The user.firstName portion of the Elvis operator example is known as the expression. If the expression evaluates to false or null, then the value after the : is returned. The two lines in the example are logically equivalent.

Safe Navigation/Dereference Operator

The safe navigation/dereference operator (?.) is used to avoid NullPointerExceptions, so it is incredibly handy. Consider the situation where you have a User object and you want to print the firstName. If the User object is null when you access the firstName property, you will get a NullPointerException. Listing 2-37 illustrates the Java way of safe dereferencing and using the Groovy safe navigation/dereference operator.

_______________

Listing 2-37. Using the Safe Navigation/Dereference Operator

User user
println user.firstName   // Throws NullPointerException

// Adding a null check, the Java way
if (user != null) {
   println "Java FirstName = ${user.firstName}"
}

// Null check the Groovy way
println "Groovy FirstName = ${user?.firstName}"

This example shows using the standard Java technique of checking for null before accessing an object and then using the Groovy safe navigation/dereference operator to accomplish the same thing.

Field Operator

In Chapter 1, you learned about properties on a class and how Groovy automatically supplies a getter. You also learned that in the event that special logic is required, you can provide your own getter.

While not recommended because it is a major violation of encapsulation, Groovy provides a way to bypass the getter and access the underlying field directly. Listing 2-38 shows an example of using the field operator (.@).

Listing 2-38. Using the Field Operator

class Todo {
   String name

   def getName() {
      println "Getting Name"
      name
   }
}

def todo = new Todo(name: "Jim")
println todo.name
println todo.@name


Getting Name
Jim
Jim

In this example, the first println uses the getter to access name, and the second println bypasses the getter to access name directly.

Method Closure Operator

Earlier in the chapter, you learned about closures and how some of the Groovy functions accept a closure as input. But what if you would like to pass a method around in the same way that you can pass a closure? Groovy provides the method closure operator (.&) for just this scenario. The method closure operator allows the method to be accessed and passed around like a closure. Listing 2-39 illustrates using a method as a closure.

Listing 2-39. Using the Method Closure Operator

def list = ["Chris","Joseph","Jim"]

// each takes a closure
list.each { println it }

String printName(String name) {
   println name
}

// & causes the method to be accessed as a closure
list.each(this.&printName)


Chris
Joseph
Jim
Chris
Joseph
Jim

This example creates a list of names and iterates through the list to print out the names. You have seen this before. A printName() method is created that prints the name parameter. Lastly and the main point of this example, the list is iterated over, executing the printName() method as a closure.

Now because this is a really simple example, you may be thinking, "Big deal." Well actually it is, especially if you are building a domain-specific language (DSL), which you will learn more about in Chapter 3.

The method really invokes System.out.println. How did the Groovy team get println to do that? The answer is that they used the method closure operator to assign System.out.println to a global property, as in def println = System.out.&println(). That is extremely powerful. Using the method closure operator, you are able to expose Java methods as closures.

Summary

The focus of this chapter was Groovy language basics. The goal was to teach you enough Groovy to get you started with Grails. In this chapter, you created a simple program (script) and compared it to what it would take to do the same thing in Java. Then you learned how to turn the script into a Groovy class, compile it, and run it using Java.

Once you learned about Groovy scripts and classes, we took a quick look at the Groovy Shell and Groovy Console. The shell and console are handy for writing and testing quick little programs. With some basic Groovy tooling under your belt, it was time to start taking a look at the Groovy language. Your journey into the Groovy language started with learning about Groovy's support of strings, closures and methods, and collections (lists, maps, sets, arrays, and ranges). Next, you had a high-level overview of Groovy's regular expression support. We covered the find (), match (==˜), and pattern (˜string) operators. Lastly, you learned about operator overloading and specialized operators. They are a major source of Groovy's power.

This chapter is by no means a comprehensive study of Groovy. Groovy is a very broad and deep topic. The goal was to give you enough Groovy knowledge to start building an application and know where to look for more information. The next chapter will introduce you to some of the Groovy frameworks and more advanced Groovy topics.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset