Groovy is Java like languages with support for both dynamic and static typing.
Groovy is a language with a turbulent history.
The short version is:
Year | Event |
---|---|
2003 | Development of Groovy starts |
2004 | JSR 241 initiated |
2005 | Development of Grails start |
2007 | Groovy version 1.0 released |
2008 | Grails version 1.0 released |
2012 | JSR 241 dormant Groovy 2.0 released |
2015 | Groovy moves from Codehaus to Apache |
Groovy is used for many different purposes:
Note: even though Grails is a huge part of Groovy usage then this article will not cover any Grails usage.
Because Groovy is so Java like then it is relative easy for Java develoeprs to start with, but it can also be a bit deceiving because the nature of the language is very different.
The entire article assumes that the reader is proficient in the Java language and everything is explained by comparing it to Java.
Besides Java syntax then some C# and Python syntax will also be shown when it helps understand Groovy.
This is not a complete reference to Groovy. This only covers some of the more important features. For more details see the Groovy Documentation or get a Groovy book.
Variable declarations are different in Groovy:
Groovy | Java equivalent | Java 10+ equivalent | C# equivalent | Python equivalent |
---|---|---|---|---|
v = 123 | N/A | var v = 123; | var v = 123; | N/A |
int v = 123 | int v = 123; | int v = 123; | int v = 123; | N/A |
final int v = 123 | final int v = 123 | final int v = 123 | N/A | N/A |
def v = 123 | N/A | N/A | dynamic v = 123 | v = 123 |
Example:
package jwj
final iv1 = 123
//iv1 = 456
iv2 = 456
println("$iv1 $iv2")
iv2 = 789
println("$iv1 $iv2")
final int iv3 = 123
//iv3 = 456
int iv4 = 456
println("$iv3 $iv4")
iv4 = 789
println("$iv3 $iv4")
//iv4 = "ABC" // Note: runtime error not compile time error
def iv5 = 123
println("$iv5")
iv5 = 456
println("$iv5")
//c = iv5.charAt(0)
iv5 = "ABC"
println("$iv5")
c = iv5.charAt(0)
println("$iv5")
It should be obvious by now that Groovy is different from Java.
There is a little trick worth knowing. Put the annotation @CompileStatic (from package groovy.transform) on a methgod, then only static typing can be used in that method, but the generated code is also much more efficient, so speedups like X10 may happen.
Groovy basically use Java type system and the way it handles null. It just have a ?. operator to handle null references.
Groovy | Java equivalent | C# equivalent |
---|---|---|
int v | int v; | int v; |
Integer v | Integer v; | int? v; |
MyClass o | MyClass o; | MyClass o; |
res = o.m() | res = o.m(); | res = o.m(); |
res = o?.m() | if(o != null) res = o.m(); else res = null; or: res = (o != null) ? o.m() : null; |
res = o?.m() |
res = a ?: b | if(a != null) res = a; else res = b; | res = a ?? b; |
Example:
package jwj
//int iv1 = null // Note: runtime error not compile time error
Integer iv2 = null
println("${iv2 == null}")
String sv = null
println("${sv == null}")
sv2 = sv?.substring(0, 1)
println("${sv2 == null}")
Groovy has the same basic types as Java.
Groovy | Java equivalent | C# equivalent |
---|---|---|
byte | byte | sbyte |
Byte | java.lang.Byte | sbyte? |
short | short | short |
Short | java.lang.Short | short? |
int | int | int |
Integer | java.lang.Integer | int? |
long | long | long |
Long | java.lang.Long | long? |
float | float | float |
Float | java.lang.Float | float? |
double | double | double |
Double | java.lang.Double | double? |
char | char | char |
Char | java.lang.Character | char? |
boolean | boolean | bool |
Boolean | java.lang.Boolean | bool? |
String | java.lang.String | string |
void | void or Void | void |
Groovy are different regarding literals.
Decimal number literals:
literal | type |
---|---|
123.456f | float |
123.456d | double |
123.456 | BigDecimal |
String literals:
literal | type |
---|---|
"ABC" | variable substitution, single line |
"""ABC""" | variable substitution, multi line |
'ABC' | no variable substitution, single line |
'''ABC''' | no variable substitution, multi line |
Arrays are also like in Java.
Example:
package jwj
byte vb = 123
println("$vb")
short vs = 123
println("$vs")
int vi = 123
println("$vi")
long vl = 123
println("$vl")
float vf = 123.456f
println("$vf")
double vd = 123.456d
println("$vd")
char vc = 'A'
println("$vc")
boolean v = true
println("$v")
String vs1 = "ABC"
println("$vs1")
String vs2 = """ABC"""
println("$vs2")
String vs3 = 'ABC'
println("$vs3")
String vs4 = '''ABC'''
println("$vs4")
BigInteger vbi = 123g
println("$vbi")
BigDecimal vbd = 123.456
println("$vbd")
Groovy has a compact way to specify list and map values.
Lists can be specified as [ value_1, ..., value_n ].
Maps can be specified as [ key_1: value_1, ..., key_n: value_n ].
Example:
package jwj
lst1 = [1, 2, 3]
println("$lst1")
println(lst1.getClass().getName())
lst2 = ["A", "BB", "CCC"]
println("$lst2")
println(lst2.getClass().getName())
mp1 = [1: "A", 2: "BB", 3: "CCC"]
println("$mp1")
println(mp1.getClass().getName())
mp2 = ["A": 1, "BB": 2, "CCC": 3]
println("$mp2")
println(mp2.getClass().getName())
Groovy has the normal arithmetic operators: +, -, *, / and %.
Note that Groovy int / produces a decimal result - to get an int result use intdiv method.
Groovy | Java equivalent | C# equivalent | Python 2.x equivalent | Python 3.x equivalent | Pascal equivalent |
---|---|---|---|---|---|
a / b | a * 1.0 / b | a * 1.0 / b | a * 1.0 / b | a / b | a / b |
a.intdiv(b) | a / b | a / b | a / b | a // b | a div b |
Groovy has the normal comparison operators: ==, !=, <, <=, > and >=.
Note that == is same value and Groovy has a separat method is for same identity.
Groovy | Java equivalent | C# equivalent |
---|---|---|
a == b | a.equals(b) | a.Equals(b) or: a == b |
a.is(b) | a == b | Object.ReferenceEquals(a, b) |
a <=> b | a.compareTo(b) | a.CompareTo(b) |
Groovy has the normal boolean operators: &&, || and !.
Groovy has the same type conversions as Java.
Groovy has the same bitwise operators as Java.
Groovy has builtin regex support.
Groovy | Java equivalent | C# equivalent |
---|---|---|
p = ~/xxxxx/ | Pattern p = Pattern.compile("xxxxx"); | Regex p = new Regex("xxxxx"); |
m = s =~ /xxxxx/ | Pattern p = Pattern.compile("xxxxx"); Matcher m = p.matcher(s); |
Regex p = new Regex("xxxxx"); MatchCollection m = p.matches(s); |
s ==~ /xxxxx/ | Pattern.matches("xxxxx", s) | Regex.IsMatch(s, "xxxxx") |
Groovy has a spread operator that can expand the elements of a list.
Groovy | Java equivalent | Java 8+ equivalent |
---|---|---|
lsta = [1, 2, 3] lstb = [*lsta, 4] |
List<Integer> lsta = Arrays.asList(1, 2, 3); List<Integer> lstb = new ArrayList<Integer>(lsta); lstb.add(4); |
List<Integer> lsta = Arrays.asList(1, 2, 3); List<Integer> lstb = new ArrayList<Integer>(lsta); lstb.add(4); |
lst = [1, 2, 3] m(*a) |
List<Integer> lst = Arrays.asList(1, 2, 3); m(lst.get(0), lst.get(1), lst.get(2)); |
List<Integer> lst = Arrays.asList(1, 2, 3); m(lst.get(0), lst.get(1), lst.get(2)); |
lstx = [new X(v: 1), new X(v: 2), new X(v: 3)] lstv = lstx*.v |
List<X> lstx = Arrays.asList(new X(1), new X(2), new X(3)); List<Integer> lstv = new ArrayList<Integer>(); for(X o : lstx) lstv.add(o.getV()); |
List<X> lstx = Arrays.asList(new X(1), new X(2), new X(3)); List<Integer> lstv = lstx.stream().map(o -> o.getV()).collect(Collectors.toList()); |
Example:
package jwj
class SomeData {
int v;
String toString() {
return "C($v)"
}
}
void f(int a, int b, int c) {
println("$a $b $c")
}
v1 = 123
v2 = ((4 * v1 + 5).intdiv(6) - 7) % 8
println("$v2")
byte bv = 123
int iv = 123
bv = iv
iv = bv
b1 = 0x0000001
b2 = b1 << 31
b3 = b1 ^ b2
b4 = ~b3
println("$b4")
iv1 = 123
iv2 = 123
println("${iv1 == iv2}")
println("${iv1.equals(iv2)}")
println("${iv1.is(iv2)}")
println("${iv1.compareTo(iv2)}")
println("${iv1 <=> iv2}")
a = 'A'
b = 'B'
c = 'C'
sv1 = "$a$b$c"
sv2 = "$a$b$c"
println("${sv1 == sv2}")
println("${sv1.equals(sv2)}")
println("${sv1.is(sv2)}")
println("${sv1.compareTo(sv2)}")
println("${sv1 <=> sv2}")
// pattern, find and match operators
def p = ~/\d+/
println(p.matcher("123").find())
println(p.matcher("ABC").find())
def m = "123 456 789" =~ /\d+/
while(m.find()) println(m.group(0))
println("123" ==~ /\d+/)
println("ABC" ==~ /\d+/)
// spread operator
lst = [new SomeData(v: 123), new SomeData(v: 456), new SomeData(v: 789)]
println(lst)
lst2 = lst*.v
println(lst2)
f(*lst2)
lst3 = [*lst2, 0]
println(lst3)
Groovy supports additional string operators on both String and StringBuffer (mutable String) for making String manipulation code simpler.
Groovy | Java equivalent |
---|---|
s = "..." ... ss = s[ix] |
String s = "..."; ... String ss = s.substring(ix, ix + 1); |
s = "..." ... ss = s[ix1..ix2] |
String s = "..."; ... String ss = s.substring(ix1, ix2 + 1); |
sb = new StringBuilder ... ss = sb[ix1..ix2] |
StringBuilder sb = new StringBuilder(); ... String ss = sb.substring(ix1, ix2 + 1); |
sb = new StringBuilder ... sb[ix1..ix2] = "..." |
StringBuilder sb = new StringBuilder(); ... sb.replace(ix1, ix2 + 1, "..."); |
sb = new StringBuilder ... sb << s1 << s2 << s3 |
StringBuilder sb = new StringBuilder(); ... sb.append(s1).append(s2).append(s3) |
Example:
package jwj
def dump(o) {
println("${o} (${o.class.name})")
}
s = "ABCD"
dump(s)
c = s[1] // a string "B" not a char 'B'
dump(c)
ss = s[1..2] // a string "BC"
dump(ss)
sb = new StringBuffer()
sb << "X" << s << "X" // "XABCDX"
dump(sb)
sb[3..3] = "X" // changes 'C' to 'X'
dump(sb)
sbs = sb[2..4] // a string "BXD"
dump(sbs)
Groovy has basically the same control structures as Java. Most noticeable difference is that do while loop is missing.
Groovy | Java equivalent |
---|---|
if(a) { foo() } else { bar() } |
if(a) { foo(); } else { bar(); } |
res = a ? b : c |
res = a ? b : c; |
switch(x) { case 1: foo() break case 2: bar() break default: throw new Exception("Ooops") } |
switch(x) { case 1: foo(); break; case 2: bar(); break; default: throw new Exception("Ooops"); } |
for(i in ia) { ... } |
for(int i : ia) { ... } |
for(i in 1..10) { ... }or: for(int i = 1; i <= 10; i++) { ... } |
for(int i = 1; i <= 10; i++) { ... } |
while(c) { ... } |
while(c) { ... } |
Groovy switch is more powerful than traditional Java switch. It accepts many different type of case values. Example:
package jwj
def dump(o) {
switch (o) {
case Integer:
switch (o) {
case 123:
println("123")
break;
case 123..125:
println("123..125")
break
case [126, 127, 128]:
println("[126, 127, 128]")
break
case { o > 1000 }:
println("Integer > 1000")
break
default:
println("Integer")
break
}
break
case String:
switch(o) {
case "ABC":
println("ABC")
break
case { o.startsWith("B") }:
println("startWithB")
break
case ~/^C.*$/:
println("Regex match")
default:
println("String")
break
}
break;
case List:
println("List")
break
default:
println("?")
break
}
}
dump(123)
dump(124)
dump(127)
dump(456)
dump(10000)
dump("ABC")
dump("BCD")
dump("CDE")
dump("EFG")
dump([1, 2, 3])
dump(new Object())
Groovy has try catch like Java but allow the use of any in catch if not interested in the actual exception. Example:
package jwj
try {
throw new IOException()
} catch(IOException ex) {
println("Got it")
}
try {
throw new IOException()
} catch(Exception ex) {
println("Got it")
}
try {
throw new IOException()
} catch(any) {
println("Got it")
}
Groovy supports both default values for arguments and call by argument name.
Groovy | Java equivalent | C# equivalent |
---|---|---|
void m(int v = 123) { ... } |
void m() { m(0); } void m(int v) { ... } |
void m(int v = 0) { ... } |
m(v: 1) |
N/A | m(v: 1); |
Note that Groovy determines choice of overloaded method based on actual type at runtime not declared type like Java.
Example:
package jwj
import groovy.transform.NamedVariant
import groovy.transform.NamedParam
void f(int a = 123, int b = 456) {
println("$a $b")
}
@NamedVariant
void f2(@NamedParam String a = "ABC", @NamedParam String b = "XYZ") {
println("$a $b")
}
void m(Object o) {
println("Object")
}
void m(String s) {
println("String")
}
v = 7
s1 = (v == 0) ? "zero" : "not zero"
println(s1)
a = [1, 2, 3]
for(i in a) {
println(i)
}
for(i = 0; i < 3; i++) {
println(a[i])
}
for(i in 1..3) {
println(i)
}
f(321, 654)
f(321)
f()
f2("CBA", "ZYX")
f2("CBA")
f2()
f2(a: "CBA", b: "ZYX")
f2(a: "CBA")
f2(b: "ZYX")
f2()
m("ABC")
Object o = "ABC"
m(o)
OO in Groovy is very similar to OO on Java.
Classes, interfaces, extends, implements, abstract and static are like in Java.
There are two major additions in Groovy compared to Java:
Groovy | Java equivalent | C# equivalent |
---|---|---|
class X { final int a = 123 int b ... } |
class X { private int a = 123; public int getA() { return a; } private int b; public int getB() { return b; } public void setB(int b) { this.b = b; } ... } |
class X { public int A { get; } = 123 public int B { get; set; } ... } |
abstract class AC { ... abstract void m() ... } class CC extends AC { ... void m() { ... } ... } |
abstract class AC { ... abstract void m(); ... } class CC extends AC { ... @Override void m() { ... } ... } |
abstract class AC { ... abstract void m(); ... } class CC : AC { ... override void m() { ... } ... } |
interface I { ... void m() ... } class C implements I { ... void m() { ... } ... } |
interface I { ... void m() ... } class C implements I { ... @Override void m() { ... } ... } |
interface I { ... void m() ... } class C : I { ... void m() { ... } ... } |
trait T { void m1() = ... } abstract class X { abstract void m1() void m2() { ... } } class Y extends X implements T { } |
N/A (interfaces with default methods in Java 8+ can be misused to achieve the same) | N/A |
Example:
package jwj
trait Tr {
void test() {
println("Test")
}
}
abstract class P {
int iv;
void dump() {
println("$iv")
}
abstract void test()
}
class C extends P implements Tr
{
String sv;
void dump() {
super.dump();
println("$sv")
}
static m() {
println("static m")
}
}
o = new C(iv: 123, sv: "ABC")
o.dump()
o.test()
o.m()
C.m()
Groovy supports operator overload.
Groovy supports extension methods (but only in use block).
Groovy | Java equivalent | C# equivalent |
---|---|---|
class X { ... X plus(X o) { ... } X minus(X o) { ... } X times(X o) { ... } X div(X o) { ... } E getAt(int ix) { ... } void putAt(int ix, E o) { ... } } |
N/A | class X { ... static X operator+(X a, X b) { ... } static X operator-(X a, X b) { ... } static X operator*(X a, X b) { ... } static X operator/(X a, X b) { ... } E this[int] { get { return ...; } set { ... value ...; } } } |
class Extensions { static def newmethod(SomeType self, ArgTyp arg) { // do whatever with self (SomeType) and arg (ArgTyp) } } |
N/A | static class Extensions { RetType newmethod(this SomeType me, ArgTyp arg) { // do whatever with me (SomeType) and arg (ArgTyp) } } |
Example:
package jwj
class MyInt {
int v;
MyInt plus(MyInt o) {
return new MyInt(v: v + o.v)
}
MyInt minus(MyInt o) {
return new MyInt(v: v - o.v)
}
MyInt multiply(MyInt o) {
return new MyInt(v: v * o.v)
}
MyInt div(MyInt o) {
return new MyInt(v: v / o.v)
}
String toString() {
return "v=$v"
}
}
class TenInt {
int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
int getAt(int ix) {
return a[ix]
}
int putAt(int ix, int e) {
a[ix] = e
}
}
class MyExtension {
static def plusPowerOf2(Integer self, int n) {
return self + (1 << n)
}
}
i1 = new MyInt(v: 1)
i2 = new MyInt(v: 2)
i3 = new MyInt(v: 3)
i7 = i3 * i2 + i1
println(i7)
ten = new TenInt()
println(ten[7])
ten[7] = 17
println(ten[7])
use(MyExtension) {
println(1.plusPowerOf2(0))
println(1.plusPowerOf2(1))
println(1.plusPowerOf2(2))
println(1.plusPowerOf2(3))
}
Besides the above extension mechanism Groovy also has a more general expanding meta class mechanism.
Basically methods and properties can be added to any class via its meta class.
Example:
package jwj
Integer.metaClass.plusPowerOf2 = { Integer n -> delegate << n }
println(1.plusPowerOf2(0))
println(1.plusPowerOf2(1))
println(1.plusPowerOf2(2))
println(1.plusPowerOf2(3))
String.metaClass {
mprint = { Integer n -> for(i in 1..n) println(delegate) }
p = "Start"
}
s = "ABC"
s.mprint(3)
println(s.p)
s.p = "Finish"
println(s.p)
s2 = "DEF"
println(s2.p)
println(s.p)
Groovy has a nice annotation for setting up delegations to real implementation class:
package jwj
interface I {
void m1()
void m2()
}
class C1 implements I {
void m1() {
println("C1.m1")
}
void m2() {
println("C1.m2")
}
}
class C2 implements I {
@Delegate
I real
void m2() {
println("Voila")
real.m2()
}
}
ox = new C2(real:new C1())
ox.m1()
ox.m2()
Groovy has a nice annotation for adding default implementations when extending an abstract class or implementing an interface:
package jwj
import groovy.transform.*
interface I {
int getInt()
String getString()
}
class C1 implements I {
int getInt() {
return 0
}
String getString() {
return null;
}
}
@AutoImplement
class C2 implements I {
}
o1 = new C1()
printf("%d %s\n", o1.getInt(), o1.getString())
o2 = new C2()
printf("%d %s\n", o2.getInt(), o2.getString())
Groovy has a nice annotation for providing equals and hashCode methods based on the properties:
package jwj
import groovy.transform.EqualsAndHashCode
class A {
int iv
String sv
}
@EqualsAndHashCode
class B {
int iv
String sv
}
a1 = new A(iv: 123, sv: "ABC")
a2 = new A(iv: 123, sv: "ABC")
println(a1 == a2)
b1 = new B(iv: 123, sv: "ABC")
b2 = new B(iv: 123, sv: "ABC")
println(b1 == b2)
Generic programming in Groovy is similar to generic programming in Java.
Groovy | Java equivalent | C# equivalent |
---|---|---|
class C<T> { ... } |
class C<T> { ... } |
class C<T> { ... } |
class C<T extends MyClass> { ... } |
class C<T extends MyClass> { ... } |
class C<T> where T : MyClass { ... } |
Example:
package jwj
class G1<T> {
T o;
}
class GP {
String toString() {
return "GP"
}
}
class GC extends GP {
String toString() {
return "GC"
}
}
class G2<T extends GP> {
T o;
}
o1int = new G1<Integer>(o: 123)
println(o1int.o)
o1infint = new G1(o: 123)
println(o1infint.o)
o1string = new G1<String>(o: "ABC")
println(o1string.o)
o1infstring = new G1(o: "ABC")
println(o1infstring.o)
//dummy = new G2(o: 123) // Note: runtime error not compile time error
//dummy = new G2(o: "ABC") // Note: runtime error not compile time error
o2p = new G2(o: new GP())
println(o2p.o)
o2c = new G2(o: new GC())
println(o2c.o)
Groovy has closures.
Groovy | Java equivalent | C# equivalent |
---|---|---|
Closure |
@FunctionalInterface interface SomeInterface { ReturnType someMethod(Type1 arg1, Type2 arg); } |
delegate ReturnType name(Type1 arg1, Type2 arg2) |
{ Type1 arg1, Type2 arg2 -> ... } |
(Type1 arg1, Type2 arg2) -> ... |
(Type1 arg1, Type2 arg2) => ... |
{ arg1, arg2 -> ... } |
(arg1, arg2) -> ... |
(arg1, arg2) => ... |
Example:
package jwj
eps = 0.0000000001d
double diff(Closure f, double x) {
return (f(x + eps) - f(x)) / eps
}
double newton_solve(Closure f) {
x = 1.0
double xold = 10.0
while(x/xold < (1 - eps) || x/xold > (1 + eps)) {
xold = x
x = x - f(x) / diff(f, x)
}
return x
}
sqrt_func = { double x -> x * x - 2 }
sqrt2 = newton_solve(sqrt_func)
println("$sqrt2 ${sqrt2 * sqrt2}")
curt3 = newton_solve({ double x -> x * x * x - 3 })
println("$curt3 ${curt3 * curt3 * curt3}")
fortg_func = { double x, double a -> x * x * x * x - a }
fort4_func = { double x -> fortg_func(x, 4.0) }
fort4 = newton_solve(fort4_func)
println("$fort4 ${fort4 * fort4 * fort4 * fort4}")
Groovy supports currying and partially applied functions. Closure has curry, rcurry and ncurry methods that return a new Closure with one argument applied.
Closure has a memoize method that direct Groovy to cache the result.
Example:
package jwj
add = { int v1, int v2 -> println("$v1 + $v2 = ${v1 + v2}") }
f1 = add.curry(123)
f1(1)
f1(2)
f1(3)
f2 = add.rcurry(123)
f2(1)
f2(2)
f2(3)
f3 = add.ncurry(0, 123)
f3(1)
f3(2)
f3(3)
f = { int v -> lbl: { println("f($v)"); v > 1 ? v + f(v - 1) : 1 }}
println(f(5))
println(f(10))
f = { int v -> lbl: { println("f($v)"); v > 1 ? v + f(v - 1) : 1 }}.memoize()
println(f(5))
println(f(10))
Groovy and Java classes with methods and property accessor/mutator are fully interoperable.
Example:
package jwj;
public class JavaClass {
private int v;
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public void m() {
System.out.printf("v=%d\n", v);
}
public void callback() {
GroovyClass og = new GroovyClass();
og.setV(123);
og.m();
}
}
package jwj
class GroovyClass {
int v;
def m( ) {
println("v=$v")
}
}
oj = new JavaClass()
oj.v = 123
oj.m()
oj.callback()
Groovy fundamentally uses the Java runtime, but Groovy has added some extra Groovy specific methods to the classes.
This include collection classes.
Example:
package jwj
lst1 = [1, 2, 3].asImmutable()
println(lst1)
println(lst1.indices.toList())
//lst1.add(4)
//lst1[1] = 0
lst2 = [1, 2, 3]
println(lst2)
println(lst2.indices.toList())
lst2.add(4)
lst2[1] = 0
println(lst2)
println(lst2.indices.toList())
map1 = [ 1: "A", 2: "BB", 3: "CCC" ].asImmutable()
println(map1)
println(map1.keySet())
println(map1.values())
//map1.put(4, "DDDD")
//map1[2] = "X"
map2 = [ 1: "A", 2: "BB", 3: "CCC" ]
println(map2)
println(map2.keySet())
println(map2.values())
map2.put(4, "DDDD")
map2[2] = "X"
println(map2)
println(map2.keySet())
println(map2.values())
Groovy also got a number of methods on collection classes for closure usage:
And Groovy got a very powerful array/list indexing feature:
Example:
package jwj
lst = [1, 2, 3, 3]
println(lst)
println(lst[1])
println(lst[1,2])
println(lst[1..2])
println(lst[0,2..3])
println(lst[-3])
println(lst[-2..-1])
println(lst.sum())
println(lst.any({ v -> v > 2}))
println(lst.any({ v -> v > 3}))
println(lst.every({ v -> v > 0}))
println(lst.every({ v -> v > 1}))
println(lst.findAll({ v -> v > 1}))
println(lst.join("#"))
lst.each({ println(" $it") })
println(lst.collect({ v -> v + 1}))
println(lst.collect({ v -> "Item: $v"}))
Groovy has builtin script capability very similar to JSR 223.
Example:
package jwj
class SomeClass {
int v;
}
def Binding bind = new Binding()
def groovy = new GroovyShell(bind)
def o = new SomeClass(v: 123)
bind.setVariable('o', o)
bind.setVariable('inval', 4)
code = """outval = o.v
println("inside")
o.v = o.v + inval"""
println(code)
groovy.evaluate(code)
def outval = bind.getVariable("outval")
println("$outval $o.v")
Groovy got some features for enabling writing DSL (Domain Specific Language) in Groovy.
The free syntax of Groovy combined with closures enables many natural language syntax features and extending the languages.
Example:
package jwj
class CI {
def moveto(int n) {
println("moveto $n")
}
}
def move(obj) { [to: { n -> obj.moveto(n) }] }
class CW {
int a
String b
}
def with(obj, clo) { obj.with(clo) }
o = new CI()
o.moveto(3)
move o to 3
w = new CW()
w.with {
a = 123
b = "abc"
println("$a $b")
}
with(w) {
a = 456
b = "def"
println("$a $b")
}
Note that Groovy actually comes with with and tap methods so no need to do this. Example:
package jwj
class X {
int a
int b
int c
int d
@Override
def String toString() { "${a} ${b} ${c} ${d}" }
}
o1 = new X()
println("o1 = ${o1}")
o1.with { a = 1; b = 2 }
o1.with { c = 3; d = 4 }
println("o1 = ${o1}")
o2 = new X()
println("o2 = ${o2}")
o2.with { a = 1; b = 2; it }.with { c = 3; d = 4 }
println("o2 = ${o2}")
o3 = new X()
println("o3 = ${o3}")
o3.tap { a = 1; b = 2 }.with { c = 3; d = 4 }
println("o3 = ${o3}")
Groovy come with several socalled builders created using Groovy DSL capabilities.
Example building JSON:
package jwj
import groovy.json.*
class IdValJson {
int id;
String val
}
idval = new IdValJson(id: 123, val: "ABC")
def json = new JsonBuilder()
json {
lbl 'demo'
data {
id idval.id
val idval.val
}
}
println(json.toString())
println(json.toPrettyString())
Output:
{"lbl":"demo","data":{"id":123,"val":"ABC"}}
{
"lbl": "demo",
"data": {
"id": 123,
"val": "ABC"
}
}
Example building XML
package jwj
import groovy.xml.*
class IdValXml {
int id;
String val
}
idvals = [ new IdValXml(id: 1, val: "A"), new IdValXml(id: 2, val: "BB"), new IdValXml(id: 3, val: "CCC") ]
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.all {
idvals.each { e ->
one {
id e.id
val e.val
}
}
}
println(writer.toString())
Output:
<all>
<one>
<id>1</id>
<val>A</val>
</one>
<one>
<id>2</id>
<val>BB</val>
</one>
<one>
<id>3</id>
<val>CCC</val>
</one>
</all>
For an example building HTML see here.
Example building Swing GUI:
package jwj
import java.awt.*
import javax.swing.*
import groovy.swing.*
class IdValSwing {
int id;
String val
}
idvals = [ new IdValSwing(id: 1, val: "A"), new IdValSwing(id: 2, val: "BB"), new IdValSwing(id: 3, val: "CCC") ]
def swing = new SwingBuilder()
swing.edt {
frame(title: 'Demo', layout : new GridLayout(1, 3), size: [300, 100], show: true) {
idvals.each { e ->
button(text:e.id, actionPerformed: { JOptionPane.showMessageDialog(null, e.val) })
}
}
}
Groovy release history:
Version | Release date |
---|---|
1.0 | January 3rd 2007 |
1.5 | December 7th 2007 |
1.6 | February 18th 2009 |
1.7 | December 22nd 2009 |
1.8 | April 27th 2011 |
2.0 | June 28th 2012 |
2.1 | January 24th 2013 |
2.2 | November 18th 2013 |
2.3 | May 5th 2014 |
2.4 | January 21st 2015 |
2.5 | May 27th 2018 |
3.0 | February 7th 2020 |
4.0 | January 25th 2022 |
The article above covers Groovy version 2.5. A few selected new features in newer versions are covered below.
Standard do while loop:
package jwj
i = 10
do {
println(i)
i--
} while(i > 0)
Java style lambdas supplementing traditional Groovy closures:
package jwj
void exec(Closure m, int v) {
m(v)
}
exec( { v -> println(v) }, 123)
exec( v -> println(v), 123)
Compatible with Java 8 lambdas.
Interface default methods:
package jwj
interface I {
default void m1() {
println("m1")
}
void m2()
}
class C implements I {
void m2() {
println("m2")
}
}
o = new C()
o.m1()
o.m2()
Compatible with Java 8 interface default methods.
Try with resources:
package jwj
class C implements Closeable {
public void close() {
println("close")
}
}
try(o = new C()) {
// nothing
}
Compatible with Java 1.7 try with resources.
Identity operators:
package jwj
class C { }
a = new C()
b = new C()
println(a === a)
println(a !== a)
println(a === b)
println(a !== b)
Safe index similar to safe dereference:
package jwj
lsta = [1, 4, 9]
lstb = null
println(lsta[1])
//NPE: println(lstb[1])
println(lsta?[1])
println(lstb?[1])
mapa = ["a": 1, "b": 4, "c": 9]
mapb = null
println(mapa["b"])
//NPE: println(mapb["b"])
println(mapa?["b"])
println(mapb?["b"])
Switch expression:
package jwj
enum E { A, B, C }
v = E.B
switch(v) {
case E.A:
println("This was A")
break
case E.B:
println("This was B")
break
case E.C:
println("This was C")
break
}
s = switch(v) {
case E.A -> "This was A"
case E.B -> "This was B"
case E.C -> "This was C"
}
println(s)
Compaitble with Java 12 switch expression.
Sealed classes with permits:
package jwj
sealed class X permits X1, X2 {
}
class X1 extends X {
}
class X2 extends X {
}
void test(X o) {
if(o instanceof X1) {
println("X1")
} else {
println("X2")
}
}
test(new X2())
Compatible with Java 15 sealed classes with permits.
GINQ (incunabting):
package jwj
class X {
int a
String b
String toString() {
return "(" + a + "," + b + ")"
}
}
lst = [ new X(a: 1, b: "A"), new X(a: 2, b: "BB"), new X(a: 3, b: "CCC") ]
println(lst)
lst2 = GQ { from elm in lst where elm.a > 1 select elm.b }
println(lst2)
class Y {
String b
double c
String toString() {
return "(" + b + "," + c + ")"
}
}
lst3 = [ new Y(b: "A", c: 1.2), new Y(b:"BB", c: 3.4) ]
println(lst3)
lst4 = GQ { from elm in lst join elm3 in lst3 on elm.b == elm3.b select elm.a, elm3.c }
for(o4 in lst4) println(o4.a + " " + o4.c)
Heavily inspired by .NET LINQ.
Contracts (incubating):
package jwj
import groovy.contracts.*
@Invariant({ a >= 0 && b >= 0 })
class X {
int a
int b
@Requires({ a >= 1 && b >= 1 })
@Ensures({ a >= 2 && b >= 2 })
def inc() {
a++
b++
}
String toString() {
return "(" + a + "," + b + ")"
}
}
o = new X(a:1, b:1)
println(o)
o.inc()
println(o)
Version | Date | Description |
---|---|---|
1.0 | March 6th 2020 | Initial version |
1.1 | March 4th 2022 | Add version history |
1.2 | November 17th 2022 | Add list/map values example and expanding meta class example |
See list of all articles here
Please send comments to Arne Vajhøj
I definitely believe that for some projects it will be beneficial to use Groovy. I am not so sure that many projects in Java today would benefit from using Groovy. To me Groovy is the right JVM language for projects where at least partial dynamic typing is considered beneficial.
Benefits of Groovy (in order of importance):
Drawbacks of Groovy (in order of importance):
So if you think Java is good for a task and you are looking for an even better language then Groovy is probably not the rigth choice - look for Kotlin or Scala instead.
But if you think Java is too heavy (too C++ish) for the task and you are looking for something ligther more like Python or VB but you need to utilize existing Java libraries, then Groovy may be perfect for you.