Groovy Code Notation: Like Java, Only Different, Page 2
Just like in Java, character data is mostly handled using the
java.lang.String class. However, Groovy provides some tweaks to make that easier, with more options for string literals and some helpful operators.
In Groovy, string literals can appear in single or double quotes. The double-quoted version allows the use of placeholders, which are automatically resolved as required. This is a GString, and that's also the name of the class involved. The following code demonstrates a simple variable expansion, although that's not all GStrings can do:
def nick = 'ReGina' def book = 'Groovy in Action, 2nd ed.' assert "$nick is $book" == 'ReGina is Groovy in Action, 2nd ed.'
As you'd expect, GStrings are pretty neat.
If you are familiar with the concept of regular expressions, you will be glad to hear that Groovy supports them at the language level.
Groovy makes it easy to declare regular expression patterns, and provides operators for applying them. Figure 1 declares a pattern with the slashy
// syntax and uses the
=~ find operator to match the pattern against a given string. The first line ensures that the string contains a series of digits; the second line replaces every digit with an x.
Figure 1. Regular expression support in Groovy through operators and slashy strings.
replaceAll is defined on
java.lang.String and takes two string arguments. It becomes apparent that
'12345' is a
java.lang.String, as is the expression
Numbers are objects
Hardly any program can do without numbers, whether for calculations or (more frequently) for counting and indexing. Groovy numbers have a familiar appearance but, unlike in Java, they are first-class objects rather than primitive types.
In Java, you cannot invoke methods on primitive types. If
x is of primitive type int, you cannot write
x.toString(). On the other hand, if
y is an object, you cannot use
In Groovy, both are possible. You can use numbers with numeric operators, and you can also call methods on number instances.
def x = 1 def y = 2 assert x + y == 3 assert x.plus(y) == 3 assert x instanceof Integer
y are objects of type
java.lang.Integer. Thus, we can use the
plus method. But we can just as easily use the
This is a surprising and major lift to object orientation on the Java platform. Whereas Java has a small but ubiquitous part of the language that isn't object oriented at all, Groovy makes a point of using objects for everything.
Using lists, maps, and ranges
Many languages, including Java, only have direct support for a single collection type -- an array -- at the syntax level and have language features that only apply to that type. In practice, other collections are widely used, and there is no reason why the language should make it harder to use those collections than arrays. Groovy makes collection handling simple, with added support for operators, literals, and extra methods beyond those provided by the Java standard libraries.
Java supports indexing arrays with a square bracket syntax, which we will call the subscript operator. Groovy allows the same syntax to be used with lists -- instances of
java.util.List -- which allows adding and removing elements, changing the size of the list at runtime, and storing items that are not necessarily of a uniform type. In addition, Groovy allows lists to be indexed outside their current bounds, which again can change the size of the list. Furthermore, lists can be specified as literals directly in your code.
The following example declares a list of Roman numerals and initializes it with the first seven numbers, as shown in figure 2.
Figure 2. An example list where the content for each index is the Roman numeral for that index.
The list is constructed such that each index matches its representation as a Roman numeral. Working with the list looks like we're working with an array, but in Groovy, the manipulation is more expressive, and the restrictions that apply to arrays are gone:
def roman = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII'] // #1 List of Roman numerals assert roman == 'IV' // #2 List access roman = 'VIII' // #3 List expansion assert roman.size() == 9
Note that there was no list item with index 8 when we assigned a value to it. We indexed the list outside the current bounds.
A map is a storage type that associates a key with a value. Maps store and retrieve values by key, whereas lists retrieve them by numeric index.
Unlike Java, Groovy supports maps at the language level, allowing them to be specified with literals and providing suitable operators to work with them. It does so with a clear and easy syntax. The syntax for maps looks like an array of key-value pairs, where a colon separates keys and values. That's all it takes.
The following example stores descriptions of HTTP return codes in a map (Hypertext Transfer Protocol, the protocol used for the World Wide Web. The server returns these codes with every response. Your browser typically shows the mapped descriptions for codes above 400.), as depicted in figure 3.
Figure 3. An example map where HTTP return codes map to their respective messages
You can see the map declaration and initialization, the retrieval of values, and the addition of a new entry. All of this is done with a single method call explicitly appearing in the source code -- and even that is only checking the new size of the map:
def http = [ 100 : 'CONTINUE', 200 : 'OK', 400 : 'BAD REQUEST' ] assert http == 'OK' http = 'INTERNAL SERVER ERROR' assert http.size() == 4
Note how the syntax is consistent with that used to declare, access, and modify lists. The differences between using maps and lists are minimal, so it's easy to remember both. This is a good example of how Groovy language designers take commonly required operations and make the programmers' lives easier by providing a simple and consistent syntax.
Although ranges don't appear in the standard Java libraries, most programmers have an intuitive idea of what a range is -- effectively a start point and an end point, with an operation to move between the two in discrete steps. Again, Groovy provides literals to support this useful concept, along with other language features such as the
for statement, which understands ranges.
The following code demonstrates the range literal format, along with how to find the size of a range, determine whether it contains a particular value, find its start and end points, and reverse it:
def x = 1..10 assert x.contains(5) assert x.contains(15) == false assert x.size() == 10 assert x.from == 1 assert x.to == 10 assert x.reverse() == 10..1
These examples are limited because we are only trying to show what ranges do on their own. Ranges are usually used in conjunction with other Groovy features.
So much for the usual datatypes. We will now come to closures, a concept that doesn't exist in Java, but which Groovy uses extensively.