© Adam L. Davis 2019
Adam L. DavisLearning Groovy 3https://doi.org/10.1007/978-1-4842-5058-7_5

5. Coming from Java

Adam L. Davis1 
(1)
New York, NY, USA
 

Since most readers are already familiar with Java, it would be helpful to compare common Java idioms with the Groovy equivalent.

Default Method Values

One thing that might surprise you coming from Java is that in Groovy you can provide default values for method parameters. For example, let’s say you have a fly method with a parameter called text:
1   def fly(String text = "flying") {println text}

Note

Using the "def" keyword in this way defines a method with return type of java.lang.Object.

This would essentially create two methods behind the scenes (from a Java standpoint):
1   def  fly() {println "flying"}
2   def  fly(String text) {println text}
This can work with any number of parameters as long as the resulting methods do not conflict with other existing methods. Given our preceding example, we can test it as follows:
fly() // output: flying
fly('Groovy') // output: Groovy

Equals, HashCode, and More

Groovy has many annotations in the "groovy.transform" package that implement AST transformations (Abstract Syntax Tree transformations). In other words, they simplify life by adding commonly written code (often called “boilerplate”) to the byte-code of your class for you at compilation time when you use them to annotate your class.

One of the tedious tasks you must often do in Java is create an equals and a hashCode method for a class. For this purpose, Groovy added the @EqualsAndHashCode annotation. Simply add it to the top of your class (right before the word class) and you’re done.

Likewise, you often want to create a constructor for all of the fields of a class. For this, Groovy has @TupleConstructor . It uses the order of the definitions of your fields to define a constructor with parameters for initializing those fields. Just add it right before your class definition.

There’s also the @ToString annotation you can add before your class definition for automatically creating a toString() method for your class. You can also configure it to include or exclude certain fields.

Finally, if you want to have all of these things on your class, just use the @Canonical annotation. You can use Ctrl+T in the groovyConsole to see how this affects the syntax tree.
1   import groovy.transform.*
2   @Canonical class Dragon {def name}
3   println new Dragon("Smaug")
4   // prints: Dragon(Smaug)
5   assert new Dragon("").equals(new Dragon(""))

../images/426440_2_En_5_Chapter/426440_2_En_5_Figa_HTML.gif Exercise

Create your own class with multiple properties using these annotations.

Regex Pattern Matching

Groovy greatly simplifies using a pattern to match text using regex (regular expressions).

Where in Java you must use the java.util.regex.Pattern class, create an instance, and then create a Matcher; in Groovy this can all be simplified to one line.

By convention you surround your regex with slashes. This allows you to use special regex syntax without using the tedious double backslash. For example, to determine if a String is an e-mail address (given a variable named email):
1   def  isEmail = email ==~ /[w.]+@[w.]+/
The equivalent code in Java would be
1   Pattern patt = Pattern.compile("[\w.]+@[\w.]+");
2  boolean  isEmail = patt.matches(email);
There’s also an operator for creating a matcher in Groovy:
1   def email = 'mailto:[email protected]'
2   def  mr = email =~ /[w.]+@[w.]+/
3   if (mr.find()) println mr.group()

This allows you to find regular expressions inside strings and to get sub-groups from a regex.

../images/426440_2_En_5_Chapter/426440_2_En_5_Figb_HTML.gif Exercise

Create a better regex for validating e-mail in Groovy.

Missing Java Syntax

Due to the nature of Groovy’s syntax and some additions to Java’s syntax over the years, Groovy was “missing” a few things before version 3.0. However, there are other ways to do the same things.

Arrays can be somewhat difficult to work with in Groovy pre-3.0 because the Java syntax for creating an array of values does not compile. For example, the following would not compile in Groovy 2.5:
1   String[] array = new  String[] {"foo", "bar"};
You should instead use the following syntax (if you must use an array):
1   String[] array = ['foo', 'bar'].toArray()
There are several options in Groovy for iterating through a list, array, or collection: using the in keyword, the Java style, or the each method with a closure. For example:
1   for (String item : array) println(item)
2   for (item in array) println(item)
3   array.each { item -> println(item) }

Optional Semicolon

Since the semicolon is optional in Groovy, this can sometimes cause line-ending confusion when you’re used to Java. Usually this is not a problem, but when calling multiple methods in a row (using a fluent API, for example), this can cause problems. In this case, you need to end each line with a non-closed operator, such as a dot.

For example, let’s take an arbitrary fluent API:
1   class Pie  {
2     def  bake() { this }
3     def  make() { this }
4     def  eat() { this }
5   }
6   def pie = new Pie().
7           make().
8           bake().
9           eat()
If you were to use the typical Java syntax, it might cause a compilation error in older versions Groovy (not true anymore):
1   def pie = new Pie() //Groovy interprets end of line
2           .make() // huh? what is this?

Optional Parenthesis Sometimes

Groovy allows parenthesis to be left out in some cases to simplify the syntax. The two main cases are as follows: when making a method call with one or more parameters and when a closure is the last (or only) parameter. For example, the following would work in Groovy:
1   // call a method named "doStuff" with parameter 1
2   doStuff 1
3   // call "doStuff" with three parameters:
4   doStuff 1, 2, 3
5   // call "each" on "list" with one Closure:
6   list.each { item -> doStuff(item) }

Where Are Generics?

Groovy supports the syntax of generics but does not enforce them by default. For this reason, you might not see a lot of generics in Groovy code. For example, the following would work fine in Groovy:
1   List<Integer> nums = [1, 2, 3.1415, 'pie']
However, Groovy will enforce generics if you add the @CompileStatic or @TypeChecked annotation to your class or method. For example:
1   import groovy.transform.*
2   @CompileStatic
3   class Foo {
4       List<Integer> nums = [1, 2, 3.1415] //error
5   }

This would cause the compilation error "[Static type checking] - Incompatible generic argument types. Cannot assign java.util.List <java.lang.Number> to: java.util.List <Integer>". Since 3.1415 becomes a java.math.BigDecimal in Groovy, the generic type of the list is automatically determined to be java.lang.Number.

Groovy Numbers

This discussion leads us to decimal numbers, which use BigDecimal by default in Groovy. This allows you to do math without rounding errors.

If you want to use double or float, simply follow your number with d or f, respectively (as you can also do in Java). For example:
1   def  pie = 3.141592d

../images/426440_2_En_5_Chapter/426440_2_En_5_Figc_HTML.gif Exercise

Try multiplying different number types and determining the class of the result.

Boolean-Resolution

Since Groovy is very similar to Java, but not Java, it’s easy to get confused by the differences. A couple of the areas of confusion are boolean-resolution (also called “Groovy truth”) and the Map syntax sugar.

Groovy is much more liberal in what it accepts in a Boolean expression. For example, null, an empty Collection, an empty String, and a zero are considered false. So, the following prints out "true" four times:
1   if ("foo") println("true")
2   if (!"") println("true")
3   if (42) println("true")
4   if (! 0) println("true")

../images/426440_2_En_5_Chapter/426440_2_En_5_Figd_HTML.gif Tip

This is sometimes referred to as “Groovy truth”.

Map Syntax

Groovy syntax sugar for maps allows you use string keys directly, which is often very helpful. However, this can cause confusion when attempting to get the class-type of a map using Groovy’s property-accessor syntax sugar (.class refers to the key-value, not getClass()). So you should use the getClass() method directly.

This can also cause confusion when you’re trying to use variables as keys. In this case, you need to surround the variables with parentheses. For example:
1   def  foo = "key"
2   def  bar = 2
3   def map = [(foo): bar]

Without the parentheses, foo would resolve to the String "foo". With the parentheses, the String "key" will be used as the key mapped to the value 2.

Summary

In this chapter, you learned about the following Groovy features:
  • You can provide default method values.

  • Various annotations that simplify life in the groovy.transform package.

  • How regular expressions are built into Groovy.

  • Different ways of defining arrays in Groovy.

  • How to use unclosed operations when writing a multiline statement.

  • Groovy uses BigDecimal by default for non-integer numbers.

  • Groovy truth.

  • You can use variable keys in the map syntax.

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

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