What is Groovy?

‹ What is Gradle? (comparison of gradle vs ant vs make) | Setting a system property in Gradle ›

I started looking at Groovy the other day while trying to understand how Gradle works.  Here are some initial impressions:

Groovy is a programming language which runs in the Java Virtual Machine.  It is similar in syntax to Java—some, but not all Java source files will also be valid Groovy source files.  It contains a lot of extra syntactic sugar, and does not subscribe to the programming language philosophy that there should be only one way to do any given thing. It is dynamically typed -- bugs where you call a non-existent method will not be caught at compile time, but instead will throw an exception during runtime.

The best place to start for someone who already knows Java seems to be the potentially mis-titled "For those new to both Java and Groovy" pages.

You can quickly write "shell scripts" in Groovy.  For example, this single line is a valid Groovy file -- it gets converted into a groovy.lang.Script class with a main() method:

System.out.println("Hello");

A given Java source file will have different behavior when run in Java than it will when run in Groovy.  For example, this program:

public class Test
{
   public static void main (String[] args)
   {
      Integer a = new Integer(1);
      Integer b = new Integer(1);
      System.out.println(String.valueOf(a == b));
   }
}

will print false when run as a Java program, but true when run as a Groovy program.

In Java, java.lang.* is imported by default.  In Groovy, many other classes and packages are imported by default.  In both cases, a locally defined class can hide an imported one. For example, this program will fail to compile in Java and will raise a groovy.lang.MissingMethodException in Groovy:

public class Math
{
   public static void main (String[] args)
   {
      System.out.println(Math.random());
   }
}

Groovy lets you call any method on any variable and won't check until runtime whether the method exists or not.  This code will print "5" when run in Groovy, but will not compile in Java:

Object a = "hello";
System.out.println(a.size());

On the other hand, this code will not compile in Java, but will happily compile in groovy and then crash with a groovy.lang.MissingMethodException when run

Object a = 1;
System.out.println(a.size());

def is a synonym for Object when declaring a variable.

def a = "hello";

Semicolons are optional, and it's actually considered idiomatic to omit them when they're not necessary.  In Groovy, after this code runs, someCompositeCalculationThatSpansSeveralLines == 5

def someCompositeCalculationThatSpansSeveralLines = 5
  + 7 + 9 + 11 + 13 + 15 + 17 + 19 + 21;

Methods and classes are public by default.  Running groovy Source.groovy will print Hello

Target.groovy

class Target
{
   static void foo()
   {
      System.out.println("Hello")
   }

Source.groovy

class Source
{
   static void main (String[] args)
   {
      Target.foo()
   }

You can define a function outside of a class:

def foo()
{
   System.out.println("Hello")
}

foo()

If a function ends without explicitly returning a value, the value of the last expression is used as the return value.

def one() // returns 1
{
   1
}

def two() // returns 2
{
   return 2
   3
}

This works with if statements:

def three(int i)  // returns either 3 or 4
{
   if (i == 3)
   {
      3
   }
   else
   {
      4
   }
}

But not with loops or switch statements:

def four() // returns null
{
   for (int i = 0; i < 5; ++i)
   {
      i
   }
}

When you call a function, the parentheses are optional if it is a top level expression and there is at least one argument and if removing them is unambiguous:

def none
def square(int c=7)
{
   c * c
}

def add(int a, int b)
{
   System.out.println "A: " + a + " B: " + b
   a + b
}

add(1, 2) // works
add 1, 2 // works
square() // works
square(add(1, 2)) // works
x = add 1, 2 // works
def y = add 1, 2 // works
square // MissingPropertyException
square(add 1, 2) // MultipleCompilationErrorsException
square add 1, 2 // MultipleCompilationErrorsException

You can create closures by enclosing code inside a curly brace pair. These are instances of anonymous subclasses of groovy.lang.Closure. A Closure can be run by calling theClosure.call(), theClosure() or, since it implements Runnable, theClosure.run().

def i = 0
def closure = {
   System.out.println("Hello #" + (++i))
}

closure() // Hello #1
closure.call() // Hello #2
closure.run() // Hello #3

Subscribe to All Posts - Wesley Tanaka