Published in 2007.
In the first part of Introduction to Groovy, we saw the first steps to code Groovy scripts, some of Groovy's advantages like closure support and some syntactic sugar, as well as basic data types. In the second part we will look at other features that make Groovy and exceptional language and a great choice for your toolbox as a Java developer, and perhaps even make it your daily language of choice.
The Groovy Truth
If you have ever coded in C, you probably will remember that conditions were evaluated in numeric context. This means that zero was treated as false and any other value was treated as true. Java defines that all conditions must be evaluated in a boolean context. That being said, if the condition does not resolves to a primitive boolean
you'll get a compile time error. Determining truth in Groovy is a little more friendly, as it is extends beyond the Java boolean context restriction. The following table summarizes the rules and the order in which they are applied to evaluate if a condition is true or false:
Type | Evaluation criteria |
---|---|
Boolean | Value is equal to true |
Matcher | There is at least one match |
Collection | The collection is not empty |
Map | The map is not empty |
String, GString | The string is not empty |
Number, Character | The value is not 0 (zero) |
otro tipo | Non null reference |
It is worth noting that the type is determined in runtime, not at compile time.
Regular Expressions
Like any other scripting or dynamic language, Groovy supports regular expressions (regexps for short) in a native way. (A discussion of what is a regular expression is beyond the scope of this article. You may look into wikipedia to get an idea if you don't know what they are). In other words you may declare them without the need for an elaborate syntax or contrived tricks (unlike Java which does require some tricks unfortunately). Just surround a regular expression with slashes and that's it, much like its done in Perl. You'll also have the following operators at your disposal:
- =~ searches for matches (returns a java.util.regex.Matcher)
- ==~ is there a match? (returns a Boolean)
- ~ returns a Pattern
Listing 3.1
def text = "Groovy is a dynamic language" assert text =~ /ua/ // true because there is a match matcher = text =~ /ua/ assert matcher instanceof java.util.regex.Matcher def match = text ==~ /.*ua.*/ assert match instanceof java.lang.Boolean assert match def pattern = ~/oovy/ def result = pattern.matcher(text) assert result instanceof java.util.regex.Matcher
The first example defines a regexp and searches for a match on the string. Because the word 'language' has the two vowels u and a together, the search is successful. The second example applies a regexp to the whole contents of text. The result is a Boolean
with Boolean.TRUE as its value because the regexp matches as well. The third example compiles a regexp down to a Pattern which will lead to improved performance if you repeat the search with the pattern several times.
Operator overloading
Since Java was released a considerable number of developers desperately wanted the ability to do operator overloading, as many came from the ranks of C++ where this feature is available. But their pleas were met with a statement on code security, which at that time seemed like a good idea and the cries slowly died out. The good news is that Groovy does support operator overloading and it does it by following a naming convention for methods. The following table summarizes some of the operators and the names you must use to overload them. There are many more than those shown (a complete list can be found on chapter 3 of "Groovy in Action", also at this link)
Operation | Method |
---|---|
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.multiply(b) |
a / b |
a.div(b) |
By just adding some of those methods to your class you automatically gain access to the overloaded operator, like in the following example:
Listing 4.1
text = "Programing" - "ing" + "er" assert "Programer" == text list = [1,2,3] list += [4,5,6] assert [1,2,3,4,5,6] == list list << 7 // list.leftShift(7) -> list.add(7) assert [1,2,3,4,5,6,7] == list s = "A" * 5 assert s == "AAAAA" s = "ABCDE" assert "BC" == s[1..2] // s.getAt([1..2]); ranged operation assert "C" == s[2] // s.getAt(2) -> s.charAt(2)
Given this feature and others found in Groovy, it is very simple to write a Domain Specific Language (DSL) which provide other advantages. We may have a look at those in another article.
From POJOs to POGOs
Without a doubt in the last years, we have seen what we commonly call POJOs (Plain Old Java Objects) take center stage in framework development and everyday use. POJOs are actually a subset of the JavaBeans convention. Yup its true, the reason being that among other things a JavaBean must publish events whenever one of its properties changes value. POJOs as they are usually coded do not follow that rule. They are indeed very useful, just take a look at well-known projects like Spring and Hibernate and you'll immediately grasp the advantages But to be true, its very tiresome to write all the getters/setters by hand. That's why some IDEs provide a code generation solution for this problem. Groovy goes beyond that and instead, skips completely to the byte code generation step to create getters/setters. All you have to do is follow the convention (now your POJOs are POGOs). To declare a POGO property you'll have to specify a type and name without access modifier, because if it has one then it will not be treated as a property but as a normal field. Let's see it in more detail, shall we?
Listing 5.1
class Person { String name String lastname private int id String toString() { "${name} ${lastname}" } } Person person = new Person( name: 'Andres', lastname: 'Almiray' ) assert "Andres Almiray" == person.toString() person.lastname = "Jaramillo" assert "Andres Jaramillo" == person.toString() person['lastname'] = "Almiray" assert "Andres Almiray" == person.toString()
Let's review the example one step at a time. First we define a POGO named Person, which has 2 properties [name,lastname] and one field [id], we know it is so because the formers do not have access modifier and the later does. In the second step, we create an instance of that class. Now notice something curious: we did not define a constructor and still we can use map-like syntax to simulate named parameters on a "default" constructor, Groovy enables it by default on POGOs. On the third step we compare the literal value of the POGO with its expected value and see that it matches the values set on the default constructor, so far so good. We then change the value of 'lastname' using dot notation, this might seem like direct field access but in fact it is not. Under the covers Groovy will use its MOP (Meta Object Protocol) to find the most suitable method to call; we might as well have used '[]' to access the property too. On the other hand if your really want to access a field directly you'll need to use '@.'. Let's see what happens when synthetic properties are added to our test class.
class Person { String name String lastname private int id String toString() { "${name} ${lastname}" } def getFullName() { "${name} ${lastname}" } } Person person = new Person( name: 'Andres', lastname: 'Almiray' ) assert person.name == "Andres" // property access assert person.@name == "Andres" // field access assert person.fullName == "Andres Almiray" try{ assert person.@fullName == "Andres Almiray" } catch( MissingFieldException e ){ println "ERROR, fullName is not a field of class Person" } // prints // "ERROR, fullName is not a field of class Person"
As you may remember, POJOs/POGOs are not JavaBeans per-se (more like a subset). That's why the Groovy team is contemplating a proposal that may see the light of day in the next major release of the language. It will enable properties to be marked with an annotation and said properties will trigger a PropertyChangeEvent whenever their values are updated:
Listing 5.3
class Person { @BoundProperty String name @BoundProperty String lastname }
This last example also shows another feature no other JVM language besides Java possesses: JDK5 annotation support. Java Generics and Enums are also on their way (update: as a matter of fact they are already in the 1.1-beta series. Since 1.1-beta-3, its possible to define Enums in Groovy though).
Conclusion
This time we have seen examples of other features of the Groovy language that will make your daily job a lot easier and pleasant. As you may have noticed by now, Groovy provides a tighter integration to the Java platform and language than other JVM languages so far. I hope that this collection of samples have poked your curiosity and incited you to continue learning Groovy.