Groovy tries to be as natural as possible for Java developers. We’ve tried to follow the principle of least surprise when designing Groovy, particularly for developers learning Groovy who’ve come from a Java background.
Here we list all the major differences between Java and Groovy.
1. Default imports
All these packages and classes are imported by default, i.e. you do not
have to use an explicit import
statement to use them:
-
java.io.*
-
java.lang.*
-
java.math.BigDecimal
-
java.math.BigInteger
-
java.net.*
-
java.util.*
-
groovy.lang.*
-
groovy.util.*
2. Multi-methods
In Groovy, the methods which will be invoked are chosen at runtime. This is called runtime dispatch or multi-methods. It means that the method will be chosen based on the types of the arguments at runtime. In Java, this is the opposite: methods are chosen at compile time, based on the declared types.
The following code, written as Java code, can be compiled in both Java and Groovy, but it will behave differently:
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o);
In Java, you would have:
assertEquals(2, result);
Whereas in Groovy:
assertEquals(1, result);
That is because Java will use the static information type, which is that o
is declared as an Object
, whereas
Groovy will choose at runtime, when the method is actually called. Since it is called with a String
, then the
String
version is called.
3. Array initializers
In Java, array initializers take either of these two forms:
int[] array = {1, 2, 3}; // Java array initializer shorthand syntax
int[] array2 = new int[] {4, 5, 6}; // Java array initializer long syntax
In Groovy, the { … }
block is reserved for closures.
That means that you cannot create array literals using Java’s array initializer shorthand syntax.
You instead borrow Groovy’s literal list notation like this:
int[] array = [1, 2, 3]
For Groovy 3+, you can optionally use the Java’s array initializer long syntax:
def array2 = new int[] {1, 2, 3} // Groovy 3.0+ supports the Java-style array initialization long syntax
4. Package scope visibility
In Groovy, omitting a modifier on a field doesn’t result in a package-private field like in Java:
class Person {
String name
}
Instead, it is used to create a property, that is to say a private field, an associated getter and an associated setter.
It is possible to create a package-private field by annotating it with @PackageScope
:
class Person {
@PackageScope String name
}
5. ARM blocks
Java 7 introduced ARM (Automatic Resource Management) blocks like this:
Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
Such blocks are supported from Groovy 3+. However, Groovy provides various methods relying on closures, which have the same effect while being more idiomatic. For example:
new File('/path/to/file').eachLine('UTF-8') {
println it
}
or, if you want a version closer to Java:
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
6. Inner classes
The implementation of anonymous inner classes and nested classes follow Java closely,
but there are some differences, e.g.
local variables accessed from within such classes don’t have to be final.
We piggy-back on some implementation details we use for groovy.lang.Closure
when generating inner class bytecode.
|
6.1. Static inner classes
Here’s an example of static inner class:
class A {
static class B {}
}
new A.B()
The usage of static inner classes is the best supported one. If you absolutely need an inner class, you should make it a static one.
6.2. Anonymous Inner Classes
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
CountDownLatch called = new CountDownLatch(1)
Timer timer = new Timer()
timer.schedule(new TimerTask() {
void run() {
called.countDown()
}
}, 0)
assert called.await(10, TimeUnit.SECONDS)
6.3. Creating Instances of Non-Static Inner Classes
In Java you can do this:
public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}
Before 3.0.0, Groovy doesn’t support the y.new X()
syntax. Instead, you have to write new X(y)
, like in the code below:
public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}
Caution though, Groovy supports calling methods with one parameter without giving an argument. The parameter will then have the value null. Basically the same rules apply to calling a constructor. There is a danger that you will write new X() instead of new X(this) for example. Since this might also be the regular way we have not yet found a good way to prevent this problem. |
Groovy 3.0.0 supports Java style syntax for creating instances of non-static inner classes. |
7. Lambda expressions and the method reference operator
Java 8+ supports lambda expressions and the method reference operator (::
):
Runnable run = () -> System.out.println("Run"); // Java
list.forEach(System.out::println);
Groovy 3 and above also support these within the Parrot parser. In earlier versions of Groovy you should use closures instead:
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
8. GStrings
As double-quoted string literals are interpreted as GString
values, Groovy may fail
with compile error or produce subtly different code if a class with String
literal
containing a dollar character is compiled with Groovy and Java compiler.
While typically, Groovy will auto-cast between GString
and String
if an API declares
the type of a parameter, beware of Java APIs that accept an Object
parameter and then
check the actual type.
9. String and Character literals
Singly-quoted literals in Groovy are used for String
, and double-quoted result in
String
or GString
, depending whether there is interpolation in the literal.
assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString
Groovy will automatically cast a single-character String
to char
only when assigning to
a variable of type char
. When calling methods with arguments of type char
we need
to either cast explicitly or make sure the value has been cast in advance.
char a='a'
assert Character.digit(a, 16)==10 : 'But Groovy does boxing'
assert Character.digit((char) 'a', 16)==10
try {
assert Character.digit('a', 16)==10
assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}
Groovy supports two styles of casting and in the case of casting to char
there
are subtle differences when casting a multi-char strings. The Groovy style cast is
more lenient and will take the first character, while the C-style cast will fail
with exception.
// for single char strings, both are the same
assert ((char) "c").class==Character
assert ("c" as char).class==Character
// for multi char strings they are not
try {
((char) 'cx') == 'c'
assert false: 'will fail - not castable'
} catch(GroovyCastException e) {
}
assert ('cx' as char) == 'c'
assert 'cx'.asType(char) == 'c'
10. Primitives and wrappers
Because Groovy uses Objects for everything, it autowraps references
to primitives. Because of this, it does not follow Java’s behavior of widening taking priority over boxing.
Here’s an example using int
int i
m(i)
void m(long l) { (1)
println "in m(long)"
}
void m(Integer i) { (2)
println "in m(Integer)"
}
1 | This is the method that Java would call, since widening has precedence over unboxing. |
2 | This is the method Groovy actually calls, since all primitive references use their wrapper class. |
11. Behaviour of ==
In Java ==
means equality of primitive types or identity for objects.
In Groovy ==
means equality in all cases.
It translates to a.compareTo(b) == 0
, when evaluating equality for Comparable
objects,
and a.equals(b)
otherwise.
To check for identity (reference equality), use the is
method: a.is(b)
.
From Groovy 3, you can also use the ===
operator (or negated version): a === b
(or c !== d
).
12. Conversions
Java does automatic widening and narrowing conversions.
Converts to |
||||||||
Converts from |
boolean |
byte |
short |
char |
int |
long |
float |
double |
boolean |
- |
N |
N |
N |
N |
N |
N |
N |
byte |
N |
- |
Y |
C |
Y |
Y |
Y |
Y |
short |
N |
C |
- |
C |
Y |
Y |
Y |
Y |
char |
N |
C |
C |
- |
Y |
Y |
Y |
Y |
int |
N |
C |
C |
C |
- |
Y |
T |
Y |
long |
N |
C |
C |
C |
C |
- |
T |
T |
float |
N |
C |
C |
C |
C |
C |
- |
Y |
double |
N |
C |
C |
C |
C |
C |
C |
- |
* 'Y' indicates a conversion Java can make, 'C' indicates a conversion Java can make when there is an explicit cast, 'T` indicates a conversion Java can make but data is truncated, 'N' indicates a conversion Java can’t make.
Groovy expands greatly on this.
Converts to |
||||||||||||||||||
Converts from |
boolean |
Boolean |
byte |
Byte |
short |
Short |
char |
Character |
int |
Integer |
long |
Long |
BigInteger |
float |
Float |
double |
Double |
BigDecimal |
boolean |
- |
B |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
Boolean |
B |
- |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
byte |
T |
T |
- |
B |
Y |
Y |
Y |
D |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Byte |
T |
T |
B |
- |
Y |
Y |
Y |
D |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
short |
T |
T |
D |
D |
- |
B |
Y |
D |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Short |
T |
T |
D |
T |
B |
- |
Y |
D |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
char |
T |
T |
Y |
D |
Y |
D |
- |
D |
Y |
D |
Y |
D |
D |
Y |
D |
Y |
D |
D |
Character |
T |
T |
D |
D |
D |
D |
D |
- |
D |
D |
D |
D |
D |
D |
D |
D |
D |
D |
int |
T |
T |
D |
D |
D |
D |
Y |
D |
- |
B |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Integer |
T |
T |
D |
D |
D |
D |
Y |
D |
B |
- |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
long |
T |
T |
D |
D |
D |
D |
Y |
D |
D |
D |
- |
B |
Y |
T |
T |
T |
T |
Y |
Long |
T |
T |
D |
D |
D |
T |
Y |
D |
D |
T |
B |
- |
Y |
T |
T |
T |
T |
Y |
BigInteger |
T |
T |
D |
D |
D |
D |
D |
D |
D |
D |
D |
D |
- |
D |
D |
D |
D |
T |
float |
T |
T |
D |
D |
D |
D |
T |
D |
D |
D |
D |
D |
D |
- |
B |
Y |
Y |
Y |
Float |
T |
T |
D |
T |
D |
T |
T |
D |
D |
T |
D |
T |
D |
B |
- |
Y |
Y |
Y |
double |
T |
T |
D |
D |
D |
D |
T |
D |
D |
D |
D |
D |
D |
D |
D |
- |
B |
Y |
Double |
T |
T |
D |
T |
D |
T |
T |
D |
D |
T |
D |
T |
D |
D |
T |
B |
- |
Y |
BigDecimal |
T |
T |
D |
D |
D |
D |
D |
D |
D |
D |
D |
D |
D |
T |
D |
T |
D |
- |
* 'Y' indicates a conversion Groovy can make, 'D' indicates a conversion Groovy can make when compiled dynamically or explicitly cast, 'T` indicates a conversion Groovy can make but data is truncated, 'B' indicates a boxing/unboxing operation, 'N' indicates a conversion Groovy can’t make.
The truncation uses Groovy Truth when converting to boolean
/Boolean
. Converting
from a number to a character casts the Number.intvalue()
to char
. Groovy constructs BigInteger
and BigDecimal
using Number.doubleValue()
when converting from a Float
or Double
, otherwise it constructs using toString()
.
Other conversions have their behavior defined by java.lang.Number
.
13. Extra keywords
Groovy has many of the same keywords as Java and Groovy 3 also has the same var
reserved type as Java.
In addition, Groovy has the following keywords:
-
as
-
def
-
in
-
trait
-
it
// within closures
Groovy is less stringent than Java in that it allows some keywords to appear in places that would be illegal in Java,
e.g. the following is valid: var var = [def: 1, as: 2, in: 3, trait: 4]
.
Never-the-less, you are discouraged from using the above keywords in places that might cause confusion even when
the compiler might be happy. In particular, avoid using them for variable, method and class names,
so our previous var var
example would be considered poor style.