108. Other Topics
This book can’t cover everything but hopefully it’s shown you a solid body of Groovy skills that will get you started. In this chapter I’ll very briefly touch on a few additional items that you may be interested in and give you some links to help you research further.
Type Checking
Groovy doesn’t check data types at compile time. Thinking of Groovy as a dynamic language helps you see why this may be the case - variables could be changing types as they move through the system and my code can use approaches such as duck typing to focus on behaviours rather than types. However, you may want to be specific about types and catch incorrect type allocations at compile time.
Consider the following code:
class Person {
String id
Integer getId() {id}
}
def pete = new Person(id: 12.3)
println pete.id
Groovy will compile this code with groovyc but when you try to run the code you’ll get a Groovy runtime exception (org.codehaus.groovy.runtime.typehandling.GroovyCastException) when we try to return 12.3 from getId - Groovy handles converting 12.3 to the id field as it’s a String but fails when trying to convert that String into an Integer. Naturally I could have written the Person class to be a lot more dynamic but you can also see that I’ve mucked around my types by declaring id as a String but return an Integer from its getter.
The @groovy.transform.TypeChecked annotation can be applied to the class to make sure types are checked at compilation time:
@groovy.transform.TypeChecked
class Person {
String id
Integer getId() {id}
}
def pete = new Person(id: 12.3)
println pete.id
Instead of a runtime exception I now get a compile-time error for trying to return an Integer from getId(). If I only want to have
type checking performed on a specific method, I can just annotate that method:
class Person {
String id
@groovy.transform.TypeChecked
Integer getId() {id}
}
def pete = new Person(id: 12.3)
println pete.id
For more information please refer to the Static type checking and Type checking extensions sections in the Groovy documentation.
Static Compilation
The @groovy.transform.CompileStatic annotation combines the functionality of @groovy.transform.TypeChecked with direct method invocation. Essentially, this removes the need for the Groovy runtime to be involved when using statically compiled classes and methods.
For more information please refer to the Static compilation section in the Groovy documentation. 10 things your static language can’t do, Compiling groovy code statically, and the Java Performance Tuning Guide are also good reads.
Metaprogramming
In the Shapes demo I touched very briefly on metaprogramming when I used the propertyMissing method to provide properties at runtime. That only glanced the surface of what’s possible and, by digging deeper you’ll discover how to:
- Use the
invokeMethodandmethodMissingmethods ofgroovy.lang.GroovyObjectto let your class handle and provide methods on-the-fly - Intercept method calls with
groovy.lang.GroovyInterceptable - Access another class’s
MetaClassto add methods
That last item lets you extend the functionality of existing classes - here’s a silly example:
Number.metaClass.addSeven << {
delegate + 7
}
Number n = 10
assert 27 == 20.addSeven()
For more information please refer to the Metaprogramming section in the Groovy documentation.
Generics
Generics allow classes, interfaces or methods to adapt to an instance-specified data type. You most often see generics used with collections such as Lists and Maps. The following snippet uses the diamond notation (<>) to indicate that the nums list should contain subtypes of Number:
List<Number> nums = [1, 2, 3, 4, 5]
However, Groovy isn’t overly respectful of generics and the following also works:
List<Number> nums = [1, 2, 3, 4, 'rabbit']
… so we can turn on type checking to catch my mistake:
@groovy.transform.TypeChecked
class NumberLister {
List<Number> nums = [1, 2, 3, 4, 'rabbit']
}
The Java Tutorial features a section on Generics and there’s a Generics in Java article in Wikipedia.
Inner Classes
Inner classes are classes that are declared within another class. Often used to improve encapsulation, you can
sometimes cause healthy debate when you ask “should I use an inner classes or a closure?”. In the example below I’ve decided that the Address inner class would be a useful way to handle the address field:
import groovy.transform.ToString
@ToString
class Person {
String name
Address address
@ToString
class Address {
String street
String suburb
String country
String prepareMailingSticker() {
"$name\n$street\n$suburb\n$country\n"
}
}
Person(name, street, suburb, country) {
this.name = name
this.address = new Address(street: street, suburb: suburb, country: coun\
try)
}
String getMailingSticker() {address.prepareMailingSticker()}
}
Person phil = new Person('Phil', '12 Smith St', 'Kimba', 'Australia')
println phil
print phil.mailingSticker
The Groovy documentation covers inner classes.
Single abstract methods
A number of classes related to responding to events implement an interface with a Single Abstract Method (SAM). Such interfaces have one method signature defined and this is usually focused on handling an event raised by an invoking class. A common example is a class such as a Button that handles user events such as a mouse click - the Button doesn’t necessarily know what you need it to do and it concerns itself more with presentation in the user interface.
Traditionally, Java developers would use what’s called an anonymous class. These are just written to handle the event but, as a class, aren’t useful as a more generic member of the codebase. This book hasn’t delved into them but Groovy supports anonymous classes and the example below will give you an indication of what one looks like:
interface Command {
public void handle(String eventTitle)
}
class Invoker {
private final commands = [ ]
void addReceiver(Command command) {
this.commands << command
}
private void event(String title) {
commands.each { cmd ->
cmd.handle(title)
}
}
void onClickEvent() {
event('Clicked')
}
void onDoubleClickEvent() {
event('Double Clicked')
}
}
def window = new Invoker()
window.addReceiver(new Command() {
public void handle(String eventTitle) {
println "I just received a '$eventTitle' event"
}
})
window.onClickEvent()
window.onDoubleClickEvent()
In the example above you’ll see that the window.addReceiver method is passed an interesting piece of syntax in new Command() {...}. An anonymous class is declared with the new keyword being invoked on an existing interface or class that the anonymous class will extend and then the body of the class is provided. For SAM interfaces this is usually what you can see in the example - a single-method anonymous class. As soon as you get an even moderately functional user interface you’ll start to see anonymous classes everywhere.
Luckily, Groovy allows you to use a closure instead of an anonymous class for SAM interfaces and this helps unclutter the code:
interface Command {
public void handle(String eventTitle)
}
class Invoker {
private final commands = [ ]
void addReceiver(Command command) {
this.commands << command
}
private void event(String title) {
commands.each { cmd ->
cmd.handle(title)
}
}
void onClickEvent() {
event('Clicked')
}
void onDoubleClickEvent() {
event('Double Clicked')
}
}
def window = new Invoker()
window.addReceiver { println "I just received a '$it' event" }
window.onClickEvent()
window.onDoubleClickEvent()
In the code above, Groovy transparently coerces the closure to the correct interface type. Prior to Groovy 2.2 you needed to cast the closure to the interface through use of the as keyword.
window.addReceiver { println "I just received a '$it' event" } as Command
The Groovy documentation has a section on SAMs and Wikipedia describes the Command Pattern on which this model of interaction is based.
Observable Maps
The ObservableMap, ObservableList and ObservableSet classes, located in the groovy.util package, can alert implementations of the java.beans.PropertyChangeListener interface when a member of the collection has changed. As PropertyChangeListener is a SAM interface, we can use closures:
PropertyChangeListenerdef myInfo = [ name: 'Jane',
pet : 'Mittens' ] as ObservableMap
myInfo.addPropertyChangeListener { evt ->
println "$evt.propertyName was changed: from $evt.oldValue to $evt.newValue"
}
myInfo.pet = 'Fido'
This models the Observer pattern.
Threads
The Thread class is used to create new execution threads in an application, allowing program tasks to work concurrently. Starting a thread requires passing the start method an implementation of java.lang.Runnable which, you guessed it, is a SAM interface:
Runnableprintln 'Start'
new Thread().start {
println 'This is a new thread'
}
println 'End'
The example above is somewhat Java-centric and Groovy’s addition of a static start(Closure closure) method to the Thread class avoids the need to call new Thread():
Thread.start {
println 'This is a new thread'
}