Groovy - Getting Started
- 15 minutes read - 3162 wordsThis article help you start with Groovy, with step by step guide and series of examples. It starts with an overview and then covers in detail examples.
Groovy Overview
Groovy is an Object Oriented Scripting Language which provides Dynamic, Easy-to-use and Integration capabilities to the Java Virtual Machine. It absorbs most of the syntax from Java and it is much powerful in terms of functionalities which is manifested in the form Closures, Dynamic Typing, Builders etc. Groovy also provides simplified API for accessing Databases and XML.
Groovy is almost compatible to Java, e.g. almost every Java construct is valid Groovy coding which makes the usage of Groovy for an experience Java programmer easy.
Features
While the simplicity and ease of use is the leading principle of Groovy here are a few nice features of Groovy:
- Groovy allows changing classes and methods at runtime. For example if a class does not have a certain method and this method is called by another class the called class can decided what to do with this call.
- Groovy does not require semicolons to separate commands if they are in different lines (separated by new-lines).
- Groovy has list processing and regular expressions directly build into the language.
- Groovy implements also the Builder Pattern which allows creating easily GUI’s, XML Documents or Ant Tasks.
- Asserts in Groovy will always be executed.
Working with Groovy
We will create a basic initial examples and understand the syntax and semantics of Groovy the scripting language.
Hello Groovy World
This example shows how to write a groovy program. It is a basic program to demo the Groovy code style.
- A Groovy source files ends with the extension
.groovy
and all Groovy classes are by default public. - In Groovy all fields of a class have by default the access modifier “private”.
package com.tk.groovy.poc
class GroovyHelloWorld {
// declaring the string variable
/** The first name. */
String firstName
String lastName
/**
* Main.
*
* @param args the args
* This is how mail function is defined in groovy
*/
static void main(def args) {
GroovyHelloWorld p = new GroovyHelloWorld()
p.firstName("Kuldeep")
p.lastName = "Singh"
println("Hello " + p.firstName + " " + p.lastName); }
}
Encapsulation in Groovy
Groovy doesn’t force you to keep the file name at the name of public class you define. In Groovy all fields of a class have by default the access modifier “private
” and Groovy provides automatically getter and setter methods for the fields. Therefore all Groovy classes are per default JavaBeans.
package com.tk.groovy.poc;
class Person {
String firstName;
String lastName;
static main(def args) {
// creating instance of person class
Person person = new Person();
// groovy auto generates the setter and getter method of variables
person.setFirstName("Kuldeep");
person.setLastName("Singh")
// calling autogenerated getter method
println(person.getFirstName() + " " + person.getLastName())
}
}
Constructors
Groovy will also directly create constructors in which you can specify the element you would like to set during construction. Groovy archives this by using the default constructor and then calling the setter methods for the attributes.
package com.tk.groovy.poc
class GroovyPerson {
String firstName
String lastName
static void main(def args) {
GroovyHelloWorld p = new GroovyHelloWorld()
//Groovy default constructor
GroovyPerson defaultPerson = new GroovyPerson();
// 1 generated Constructor
// similar constructor is generated for lastName also
GroovyPerson personWithFirstNameOnly = new GroovyPerson(firstName :"Kuldeep")
println(personWithFirstNameOnly.getFirstName());
// 2 generated Constructor
GroovyPerson person = new GroovyPerson(firstName :"Kuldeep", lastName :"Singh")
println(person.firstName + " " + person.lastName)
}
}
Groovy Closures
A closure in Groovy is an anonymous chunk of code that may take arguments, return a value, and reference and use variables declared in its surrounding scope. In many ways it resembles anonymous inner classes in Java, and closures are often used in Groovy in the same way that Java developers use anonymous inner classes. However, Groovy closures are much more powerful than anonymous inner classes, and far more convenient to specify and use.
Implicit variable: ‘it
’ variable is defined which has a special meaning. Closure in this case takes as single argument.
package com.tk.groovy.poc
class GroovyClosures {
/**
* Main Function.
*
* @param args the args
*/
def static main(args) {
/**
* Defining closure printSum
*
* -> : is a special keyword here
*/
def printSum = { a, b -> println a+b }
printSum(5,7)
/**
* Closure with implicit argument
*/
def implicitArgClosure = { println it }
implicitArgClosure( "I am using implicity variable 'it'" )
}
}
Closures are very heavily used in Groovy. Above example is very basic example. For deep understanding of closure kindly read : http://groovy.codehaus.org/Closures+-+Formal+Definition
Methods and Collections
Below program shows how to declare methods and how to use collections in groovy and some basic operations on collections.
- Groovy has native language support for collections, lists, maps sets, arrays etc. - Each collection can be iterated either by assigning the values variable, Or by using implicit variable ‘it’
- Groovy also have Range collection which can be directly used to include all values in a range
- A Groovy Closure is like a “code block” or a method pointer. It is a piece of code that is defined and then executed at a later point. It has some special properties like implicit variables, support for currying and support for free variables
package com.tk.groovy.poc
class GroovyCollections {
def listOperations(){
println("Operation over LIST: ")
// defining the Integer type list using generic
List list = [1, 2, 3, 4]
// Note this is same as below declaration
// Here we are explicitly declaring the type of list
//List<Integer> list = [1, 2, 3, 4]
println("Iterating the list using variable assignment: ")
// using .each method to iterate over list
// 'itemVar' is implicitly declared as a variable of same type as list
// with each block, values will be assigned to 'itemVar'
// this is syntax of groovy Closure
list.each{itemVar-> print itemVar + ", " }
println("")
println("Iterating the list using implicit variable 'it': ")
// inspite of declaring the variable explicitly directly use
// the implicit variable 'it'
list.each{print it + ", "}
}
def mapOpertion(){
println("Operation over MAP: ")
def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]
println("Iterating the map using variable assignment: ")
map.each{k,v->
print k + ", "
println v
}
println("")
println("Iterating the map using implicit variable 'it': ")
map.each{
print it.key + ", "
println it.value
}
}
def setOperations(){
Set s1 = ["a", 2, "a", 2, 3]
println("Iterating the map using variable assignment: ")
s1.each{setVar-> print setVar + ", "}
println("")
println("Iterating the set using implicit variable 'it': ")
s1.each{ print it + ", "}
}
def rangeOperations() {
def range = 1..50
//get the end points of the range
println()
println ("Range starts from: " + range.from + " -- Range ends to: " + range.to)
println("Iterating the range using variable assignment: ")
range.each{rangeVar-> print rangeVar + ", "}
println("")
println("Iterating the range using implicit variable 'it': ")
range.each{ print it + ", "}
}
/**
* Main Method
*
* @param args the args
*
* This is how we declare a static method in groovy
* by using static keyword
*/
static main(def args){
GroovyCollections groovyCol = new GroovyCollections()
groovyCol.listOperations();
println("")
groovyCol.mapOpertion();
println("")
groovyCol.setOperations();
println("")
groovyCol.rangeOperations();
}
}
File operations & Exception handling
- Exceptions in groovy: groovy doesn’t actually have checked exceptions. Groovy will pass an exception to the calling code until it is handled, but we don’t have to define it in our method signature
package com.tk.groovy.poc
class GroovyFileOperations {
private def writeFile(def String filePath, def String content) {
try {
def file = new File(filePath);
// declaring out variable using groovy closer
// writing content using groovy implicit out variable
file.withWriter { out ->
out.writeLine(content)
}
// all keyword defines all exceptions
} catch (All) {
// print the stack trace
println(All)
}
}
private def readFile(def String filePath) {
def file = new File(filePath);
file.eachLine { line -> println(line) }
}
def static main(def args) {
def filePath = LICENCE.txt;
def content = "testContent"
GroovyFileOperations fileOp = new GroovyFileOperations();
println("Writing content in the file")
fileOp.writeFile(filePath, content);
println("Content from the file")
fileOp.readFile(filePath);
}
}
Dynamic method invocation
Using the dynamic features of Groovy calling method by its name only, Defining static methods
package com.tk.groovy.poc
class Dog {
/**
* Bark.
*/
def bark() {
println "woof!"
}
/**
* Sit.
*/
def sit() {
println "sitting"
}
/**
* Jump.
*/
def jump() {
println "boing!"
}
/**
* Do action.
*
* @param animal the animal
* @param action the action
*
* This is how we define the static method
*/
def static doAction( animal, action ) {
//action name is passed at invocation
animal."$action"()
}
/**
* Main.
*
* @param args the args
*/
static main(def args) {
def dog = new Dog()
// calling the method by its name
doAction( dog, "bark" )
doAction( dog, "jump" )
}
}
Groovy and Java Inter-Operability
This example shows how we can use groovy script program with java programs. There are two different ways shown below to integrate the groovy script with java program-
- Using
GroovyCloassLoader
: GroovyCloassLoader load scripts that are stored outside the local file system, and finally how to make your integration environment safe and sandboxed, permitting the scripts to perform only the operations you wish to allow - Directly Injecting
GroovyClassObject
– We can directly create the object of Groovy script class and then can perform the operation as needed.
Groovy Class:
package com.tk.groovy.poc.groovy
import com.tk.groovy.poc.java.JavaLearner
/**
* The Class GroovyPerson.
*/
class GroovyLearner {
def printGroovy() {
println("I am a groovy Learner")
}
static void main(def args) {
JavaLearner javaLearner = new JavaLearner();
javaLearner.printJava();
}
}
Java Class:
package com.tk.groovy.poc.java;
import groovy.lang.GroovyClassLoader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import org.codehaus.groovy.control.CompilationFailedException;
import com.nagarro.groovy.poc.groovy.GroovyLearner;
/**
* The Class JavaLearner.
*/
public class JavaLearner {
/**
* Prints the java.
*/
public void printJava() {
System.out.println("I am a java learner");
}
/**
* The main method.
*
* @param args
* the arguments
*/
public static void main(String[] args) {
// using GroovyCloassLoader class
String relativeGroovyClassPath = "src\\com\\tk\\groovy\\poc\\groovy\\GroovyLearner.groovy";
try {
Class groovyClass = new GroovyClassLoader().parseClass(new File(relativeGroovyClassPath));
Object groovyClassInstance = groovyClass.newInstance();
groovyClass.getDeclaredMethod("printGroovy", new Class[] {}).invoke(groovyClassInstance, new Object[] {});
} catch (CompilationFailedException | IOException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
// directly calling groovy class in java
// this work on the fact that both groovy and java are compiled to
// .class file
GroovyLearner groovyLearner = new GroovyLearner();
groovyLearner.printGroovy();
}
}
Building Groovy Applications
Below section demonstrates the use of Groovy script with Maven, Ant, and logging library –log4j.
Integration with Ant:
Below example shows you how easy it is to enhance the build process by using Groovy rather than XML as your build configuration format.
Central to the niftiness of Ant within Groovy is the notion of builders. Essentially, builders allow you to easily represent nested tree-like data structures, such as XML documents, in Groovy. And that is the hook: With a builder, specifically an Ant-Builder, you can effortlessly construct Ant XML build files and execute the resulting behavior without having to deal with the XML itself. And that’s not the only advantage of working with Ant in Groovy. Unlike XML, Groovy is a highly expressive development environment wherein you can easily code looping constructs, conditionals, and even explore the power of reuse, rather than the cut-and-paste drill you may have previously associated with creating new build.xml files. And you still get to do it all within the Java platform!
The beauty of builders, and especially Groovy’s Ant Builder, is that their syntax follows a logical progression closely mirroring that of the XML document they represent. Methods attached to an instance of Ant Builder match the corresponding Ant task; likewise, those methods can take parameters (in the form of a map) that correspond to the task’s attributes. What more, nested tags are such as includes and file-sets are then defined as closures.
package com.tk.groovy.project
/**
* The Class Build.
*/
class Build {
def base_dir = "."
def src_dir = base_dir + "/src"
def lib_dir = base_dir + "/lib"
def build_dir = base_dir + "/classes"
def dist_dir = base_dir + "/dist"
def file_name = "Fibonacci"
def ant=null;
void clean() {
ant.delete(dir: "${build_dir}")
ant.delete(dir: "${dist_dir}")
}
/**
* Builds the.
*/
void build() {
ant= new AntBuilder().sequential {
taskdef name: "groovyc", classname: "org.codehaus.groovy.ant.Groovyc"
groovyc srcdir: "src", destdir: "${build_dir}", {
classpath {
fileset dir: "${lib_dir}", { include name: "*.jar" }
pathelement path: "${build_dir}"
}
javac source: "1.7", target: "1.7", debug: "on"
}
jar destfile: "${dist_dir}/${file_name}.jar", basedir: "${build_dir}",filesetmanifest: "mergewithoutmain",{
manifest{
attribute( name: 'Main-Class', value: "com.tk.groovy.Fibonacci" )
}
}
java classname:"${file_name}", fork:'true',{
classpath {
fileset dir: "${lib_dir}", { include name: "*.jar" }
pathelement path: "${build_dir}"
}
}
}
}
/**
* Run.
*
* @param args the args
*/
void run(args) {
if ( args.size() > 0 ) {
invokeMethod(args[0], null )
}
else {
build()
}
}
/**
* Main.
*
* @param args the args
*/
static main(args) {
def b = new Build()
b.run(args)
}
}
Integration with Maven:
An approach to Groovy and Maven integration is to use the gmaven plugin for Maven. This example is made by using
These examples could be useful in any project where we want to do some kind of sanitary test over our ear file and can perform handy things at the build time.
For maven to compile and understand the Groovy class files, we have to follow the same folder layout as we used to do for java project with only few differences.
project/
|
+- |
+- pom.xml
|
|
+- src/main/resources/
|
|
+- src/main/groovy/
| |
| +- com/nagarro/groovy
| |
| +- XXX.groovy
|
+- src/main/test/
|
+- com/nagarro/groovy
|
+- XXXTest.Groovy
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>GroovyMavenPOC</groupId>
<artifactId>GroovyMavenPOC</artifactId>
<name>Example Project</name>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy.maven.runtime</groupId>
<artifactId>gmaven-runtime-1.6</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Groovy File
package com.tk.groovy.poc
import groovy.util.GroovyTestCase
class HelloWorldTest
extends GroovyTestCase
{
void testShow() {
new HelloWorld().show()
}
}
package com.tk.groovy.poc
import groovy.util.GroovyTestCase
/**
* The Class HelperTest.
*/
class HelperTest
extends GroovyTestCase
{
void testHelp() {
new Helper().help(new HelloWorld())
}
}
Performance Analysis
This POC shows how significant the performance improvements in Groovy 2.0 have turned out and how Groovy 2.0 would now compare to Java in terms of performance.
In case the performance gap meanwhile had become minor or at least acceptable it would certainly be time to take a serious look at Groovy.
The performance measurement consists of calculating Fibonacci numbers with same code in Groovy script and Java Class file.
All measurements were done on an Intel Core i7-3720QM CPU 2.60 GHz using JDK7u6 running on Windows 7 with Service Pack 1. I used Eclipse Juno with the Groovy plugin using the Groovy compiler version 1.8.6.xx-20121219-0800-e42-RELEASE.
Fibonacci Algorithm
Java Program
package com.nagarro.groovy.poc;
public class JavaFibonacci {
public static int fibStaticTernary(int n) {
return (n >= 2) ? fibStaticTernary(n - 1) + fibStaticTernary(n - 2) : 1;
}
public static int fibStaticIf(int n) {
if (n >= 2)
return fibStaticIf(n - 1) + fibStaticIf(n - 2);
else
return 1;
}
int fibTernary(int n) {
return (n >= 2) ? fibStaticTernary(n - 1) + fibStaticTernary(n - 2) : 1;
}
int fibIf(int n) {
if (n >= 2)
return fibIf(n - 1) + fibIf(n - 2);
else
return 1;
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
JavaFibonacci.fibStaticTernary(40);
System.out.println("Java(static ternary): " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
JavaFibonacci.fibStaticIf(40);
System.out.println("Java(static if): " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
new JavaFibonacci().fibTernary(40);
System.out.println("Java(instance ternary): " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
new JavaFibonacci().fibIf(40);
System.out.println("Java(instance if): " + (System.currentTimeMillis() - start) + "ms");
}
}
Groovy Program
package com.tk.groovy.poc
class GroovyFibonacci {
static int fibStaticTernary (int n) {
n >= 2 ? fibStaticTernary(n-1) + fibStaticTernary(n-2) : 1
}
static int fibStaticIf (int n) {
if(n >= 2) fibStaticIf(n-1) + fibStaticIf(n-2) else 1
}
int fibTernary (int n) {
n >= 2 ? fibTernary(n-1) + fibTernary(n-2) : 1
}
int fibIf (int n) {
if(n >= 2) fibIf(n-1) + fibIf(n-2) else 1
}
public static void main(String[] args) {
def warmup = true
def warmUpIters = 50
def start = System.currentTimeMillis()
JavaFibonacci.fibStaticTernary(40)
println("Groovy(static ternary): ${System.currentTimeMillis() - start}ms")
start = System.currentTimeMillis()
JavaFibonacci.fibStaticIf(40)
println("Groovy(static if): ${System.currentTimeMillis() - start}ms")
def fib = new JavaFibonacci()
start = System.currentTimeMillis()
fib.fibTernary(40)
println("Groovy(instance ternary): ${System.currentTimeMillis() - start}ms")
start = System.currentTimeMillis()
fib.fibIf(40)
println("Groovy(instance if): ${System.currentTimeMillis() - start}ms")
}
}
See below table for performance metric s of above two programs:
Groovy 2.0 | Java | |
---|---|---|
static ternary | 1015ms | 746ms |
static if | 905ms | 739ms |
instance ternary | 708ms | 739ms |
instance if | 924ms | 739ms |
IO Operations
Now another performance test is done on I/O operations by Groovy and Java. See below the respective programs and performance metric table.
All measurement were done using previous configuration with additional file specification, we have used test.txt file with below specification; No of Lines- 5492493 Size of File- 243 Mega Bytes
Java File Read
package com.tk.groovy.poc;
public class ReadFileJava {
private static String FILE_NAME=LICENCE.txt;
public static void readFile() {
BufferedReader br = null;
try {
File file=new File(FILE_NAME);
double bytes = file.length();
double kilobytes = (bytes / 1024);
double megabytes = (kilobytes / 1024);
br = new BufferedReader(new FileReader(file));
int count = 0;
while (br.readLine() != null) {
count++;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
readFile();
long end = System.currentTimeMillis();
System.out.println("Time Consumed------>>>>" + (end - start)+"ms");
}
}
Groovy File Read
package com.tk.groovy.poc
class GroovyReadFile {
static main(def a){
def filePath = "D:\\test.txt"
long start = System.currentTimeMillis();
int count = 0;
new File(filePath).eachLine{
count++;
}
println count;
long end = System.currentTimeMillis();
println ("Time Consumed------>>>>"+(end - start) +"ms");
}
}
Performance metric table:
Groovy 2.0 | Java | |
---|---|---|
Iteration 1 | 2848ms | 1667ms |
Iteration 2 | 2649ms | 1578ms |
Iteration 3 | 2692ms | 1811ms |
Iteration 4 | 2686ms | 1799ms |
Conclusion
I would like to see some benefits in using Groovy where appropriate and perhaps better than Java. But a lot of the “features” of dynamic languages appear to me as disadvantages. For example all the useful compile time checks and IDE support (auto completion etc.) are missing or not really good. Code in Groovy (and other similar languages) often may be much shorter or concise than the same code in a language like Java.
The times where Groovy was somewhat 10-20 times slower than Java are definitely over whether @CompileStatic
is used or not. This means to me that Groovy is ready for applications where performance has to be somewhat comparable to Java
So I see Groovy’s place where you have to script a fast and small solution to a specific problem as we its use is shown with Maven integration. References
- http://groovy.codehaus.org/Beginners+Tutorial
- http://www.vogella.com/articles/Groovy/article.html
- http://groovy.codehaus.org/Getting+Started+Guide
- http://groovy.codehaus.org/Dynamic+Groovy
- http://groovy.codehaus.org/Closures
- http://groovy.codehaus.org/Closures+-+Formal+Definition
This article is also supported by my friend Dhruv Bansal
#groovy #java #introduction #language #comparison #technology