Groovy coding conventions

At work we are using Groovy language to write the Cucumber acceptance tests. Groovy turns out to be very nice language with flat learning curve for Java developer. There are many interesting features in the language, but I believe some of them have to be used with caution.
As we work more with Groovy and learn more of its features we found there are many ways of doing the same thing. It is useful, because it makes code more expressive, but it can also be an issue when there are multiple developers working on the same code base. This problem most probably exists in every programming language and it can be partially solved by agreeing on the coding conventions. Unfortunately there is no established code convention for Groovy yet as there are for other languages like Java.
In this post I list some features of the Groovy language and try to choose a convention to use. Of course this is not the only correct approach. This is more a try to orginise the conventions and keep it for future reference.

This post format is:

Feature/Property name

Description of the feature/property (facts only no personal opinion)

Chosen convention with description (chosen by personal opinion)


Semicolons

Description

Semicolons at the end of the line are optional in Groovy.

Convention

No semicolons when only one statement in the line.

Types

Description

Groovy allows to not specify types of:

  • values returned from method
  • parameters of the method
  • local variables
  • class field

When using “def” in Groovy, the actual type holder is “Object”. In method definition we can omit both parameters’ types and “def” to make the parameter the type of “Object”.
It is easier to work with the code when all types are specified because the IDE can then suggest possible methods and fields of the obejcts.

Convention

Values returned from the method

Always specify the type of the value returned from the method or use “void” when no value is returned.

Parameters of the method

Always specify the types of the method’s parameters.

Local variable

Specify the type of the local variables when it makes it more readable (eg. in methods longer than 10 lines). If the type of the local variable is obvious it can be declared with “def” keyword. In simple cases IDE can figure out what is the type of the object.

Class field

Always specify the type class’ fields.

Return keyword in the methods

Description

“Return” keyword is optional in Groovy. Methods will return the value returned by the last run expression in the method. Unfortunatelly it is not always clear which one is the “last run expression in the method”. It can lead to confusion especially when returned value type for the method is not specified. Method can then return some value when we expect it to not return anything.

Convention

Always use “return” keyword in the method which is expected to return some value.

Access modifiers

Description

In Groovy all class fields are by default private and all methods are by default public. Because of that there is no need to add “public” in keyword for the methods and “private” keyword for the fields.
For all private fields Groovy creates basic getters and setters in the runtime so there is no need to do it manually. It is still possible to create getter or setter manually if we want to some specific logic for it.

Convention

Methods

Do not specify methods as “public”. Leave it without access modifier instead.

Fields

Do not specify fields as “private”. Leave it without access modifier instead.
Do not create simple getters and setters for fields. Groovy creates them in the runtime.

Class objects

Description

Classes are first-class citizens in Groovy. There is no need to use “.class” suffix. It is perfectly fine to specify just a class name.

Convention

Do not use “.class” suffix when you need Class object. Instead just write the name of the class.

Fields access

Description

In Groovy there is a shortcut notation to access and set fields of the class. Instead of using getters and setters it is possible to use field-like access notation:

resourceGroup.getResourcePrototype().getName() == SERVERTYPENAME
resourceGroup.resourcePrototype.name == SERVERTYPENAME

resourcePrototype.setName(“something”)
resourcePrototype.name = “something”

When using field-like notation setters and getters are invoked under the hood. So in case there will be more sophisticated than basic setter created in future for the field it will be still invoked with field-like access.

Convention

Always use field-like notation instead of setters and getters (e.g. customer.id = 3 instead of customer.setId(3)) the effect is always the same – calling the setter (created or auto-generated).

Default method’s parameters

Description

It is possible in Groovy to specify default values of the paramters in the method.

void createNewCustomer(int id, String name, int status = 1) {...}
...
createNewCustomer(123, "customerName")

In the above example “createNewCustomer” method has optional third parameter. If it is not specified the value of the parameter is “1”.

Convention

Instead of overloading methods and invoking one from another use default parameter feature.
E.g.
void createNewCustomer(int id, String name, int status = 1) {...}
instead of

void createNewCustomer(int id, String name, int status) {...}
void createNewCustomer(int id, String name) {
  createNewCustomer(id, name, 1)
}

Equals and ==

Description

In Groovy “==” operator is the same as “.equals(..)” method in Java. If it is required to check if the reference to the object is the same (like with “==” in Java) you have to use object.is(..) method.

Convention

For checking if objects are equal use “==” operator instead of object.equals(..) method.

String and GString

Description

In Groovy it is possible to use parameters in the GString instead of concatenating Strings to put some parameters in it.
E.g. "this string needs variable: ${variable}" instead of "this string needs a variable: " + variable
Every string which is created using quotes is a GString objects which accepts a variable in the form with dollar sign. Braces are optional when there is only a simple variable, but they are required when there is some operation (like getter invocation) needed.
To create String object specify the object in the single quote. Using it when there is no variables in the string has positive impact on performance.
'This is String object'
Because semicolons are optional in Groovy, to create multiline String or GString triple single quote or triple quotes have to be used.

def gstring = """This is multiline
GString object in Groovy"""

def string = '''This is multiline
String object in Groovy''';

Convention

Use quotes (GString) whenever there is need to inject some parameter into the string.
When there is a constant or a string without any parameters use single quote to create String object.

Collections

Description

Groovy has simplified syntax to create lists and maps. To create a list you have to put objects in the sqare brackets.
def listOfStrings = ["abc", "def", "ghi"]
To create map you have to specify key and values separeted by the colon.
def mapOfStringToString = [firstKey: "1value", secondKey: "2value"]
Groovy assume that key in the Map is “String” by default. To insert a variable or object as a key wrap it with the brackets “()”.

def keyForTheMap = 'firstKey'
def mapOfStringToString = [(keyForTheMap): "1value", secondKey: "2value"]

Groovy has also its own syntax to add elements to maps and lists.

def map = [first : “foo”]
map << [second: “bar”]

def list = [1,2,3,4]
list << 5

To check if collection contains the element it is possible to use „in” parameter. E.g. 4 in list instead of list.contains(4).

Groovy adds the functionality called “Lambda expressions” or “Closures”. These are especially useful when working on the collections. Instead of iterating through the collection you can use one of the available methods accepting function as a parameter (each{}, find{}, findAll{}, every{}, collect{}, inject{}).

Convention

Use native Groovy collection syntax to create Maps and Lists instead of creating it with new ArrayList() / new HashMap().
Use Java native methods (add(..) and put(..)) to add elements to lists and maps.
Use Java syntax with collection.contains(element) method to check if element is part of the collection.
Prefer to use Groovy collections closures (each{}, find{}, findAll{}, every{}, collect{}, inject{}) over looping when this makes code more readable (probably in most cases)

Groovy Truth

Description

In Groovy all objects can be ‘coerced’ to a boolean value: everything that’s null, void or empty evaluates to false, and if not, evaluates to true.
This means if (name != null && name.length > 0) {} is the same as if (name) {}

Convention

Always use full condition to check if object is non-empty. E.g. write if (name != null && name.length > 0) {} instead of if (name) {}. Result of testing just the name (which is String) is not obvious.
Alternativly you can use safe dereference operator to ommit checking null value e.g. if (name?.length > 0) {}

Groovy safe dereference and Elvis operators

Description

Groovy supports the variant of the “.” operator to safely navigate an object graph.
E.g. instead of: if (customer != null) { name = customer.name } else { name = null } you can write def name = customer?.name.
Groovy adds “Elvis” operator to set value of ease the use of ternary operator in some common cases. So instead of writing def result = name != null ? name : "Unknown" it is possible to write def result = name ?: "Unknown".

Convention

Use safe deference operator “?.” to get property of the object which can be a null instead of testing if it is null with if condition (e.g. “order?.customer?.address”).
Use Elvis operator to set default value for variables (e.g. def result = name ?: "Unknown" instead of def result = name != null ? name : "Unknown").

References:
When writing this posts I was using this two articles as a reference:
[1] http://groovy.codehaus.org/Groovy+style+and+language+feature+guidelines+for+Java+developers
[2] http://java.dzone.com/articles/groovy-coding-style

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *