It seems like it was only yesterday that I was introduced to groovy. I had been a fairly successful Java developer for well over ten years and one day one of my co-workers was like “hey check out this groovy thing its like java…but better.”

I was hooked almost immediately.

I loved how easily I could create a simple Java bean with setters, getters, equals, hashCode, with a meaningful toString and all this without littering my codebase with IDE generated code.

class SimpleBean {

int value1

String value2

}

I marveled at the simplicity of the map constructor:

new SimpleBean(

value1: 5,

value2: "hello",

)

and of the list and map initializers:

List<String> strings = ['s1', 's2', 's3']

Map<String, Integer> maps = [

s1: 25,

s2: 50,

]

And string interpolation, or sweet string interpolation:

SimpleBean sb = new SimpleBean(

value1: 5,

value2: "hello",

)



String message = "${sb.value1} some other string stuff ${sb.value2}"

It seemed like I couldn’t go a day without finding something awesome about groovy.

Photo by freestocks.org from Pexels

I could go on and on.

I was in love.

I quickly preached to all my friends how superior groovy was to their primitive Java. I swore I would never use Java every again.

Why should I, I had groovy. We were made for each other.

Photo by Johannes Plenio from Pexels

The trouble started around the time Java 8 came out. I should have noticed the warnings signs but I was blinded by my love of Groovy.

For those of you not in the know, Java 8 was (IMHO) probably one of the biggest releases of Java since 1.5. It introduces lots of new language constructs including:

Being a good Java developer and wanting to always try the new thing I went about using this new stuff… in my groovy code.

by Pixabay from Pexels

Lambda’s and Streams

Lambda’s and streams go together like peanut butter and chocolate. They make life worth living:

List<SimpleBean> myList = new ArrayList<>();

myList.stream()

.filter(b -> b.getValue1() > 5)

.map(SimpleBean::getValue2)

.collect(Collectors.toList());

Groovy had lamda’s and, kind of, had streams too. But I wanted to be like the cool kids and use the new Java 8 stuff, but in Groovy.

List<SimpleBean> myList = []

myList.stream()

.filter({ SimpleBean b -> b.value1 > 5 } as Predicate)

.map({SimpleBean b -> b.value2} as Function)

.collect(Collectors.toList())

What was that!?! What happened to my simple sweet groovyness. Groovy uses Closures for EVERYTHING! Which means, when you want to use a generically typed interface like Predicate<?> or Function<?,?> you need to type cast your untyped closure in order for it to work.

GString vs String

As mentioned eariler String interpolation is awesome. I love it. But groovy does something strange.

"${sb.value1} some other string stuff ${sb.value2}"

This is not a string. Its a GString. It does not evaluate the string until it needs to render. In theory this means if the sb.value1 changed I could use the same GString and display different values. In reality, I have NEVER needed this functionality. In practice, it has meant that many times I have had to help groovy and type cast a string to a string. ???

"${sb.value1} some other string stuff ${sb.value2}" as String

Weird right?

Type Checking

At first I felt vindicated. At last, I can be like those egotistical Python programmers and laugh at Java and its silly type checking. Who needs it, I have groovy now. def is all I need.

Then I started getting runtime exceptions:

Unknown method

No such property

Can not convert between…

Oh, right.

Type checking would have caught all that. Well that’s ok, I’ll use Groovy’s built in type check and start annotating all my code with

@CompileStatic

That should solve it. And in it way it did. But now Groovy enforced type checking. I was putting in weird things like:

.setList([] as List<String>)

I could do that very simply with Java:

.setList(Collections.emptyList())

Java here was type safe and immutable.

Hmm, about immutable…

by Pixabay From Pexels.com

Immutable

Most people would agree that an immutable object consists of:

An object where the state is initialized once

The state can not change

Copy on write

At first one would think, oh that’s easy to do in groovy.

class SimpleBean {

final int value1

final String value2

}

Done.

Only, not. We need a constructor since the default constructor won’t work.

class SimpleBean {

final int value1

final String value2

SimpleBean (int value, String value2) {

this.value1 = value1

this.value1 = value2

}

}

Now done? Well, not really. You see by providing a constructor you made it so the map constructor no longer works. So you really need to provide a map constructor…by hand.

class SimpleBean {

final int value1

final String value2

SimpleBean (int value, String value2) {

this.value1 = value1

this.value1 = value2

}

SimpleBean(Map<?,?> values) {

this.value1 = values['value1'] as int

this.value2 = values['value2']

}

}

Now we’re finally done right? Well, not really. While this might work we didn’t really implement the map constructor the way we should have, such as protecting against unknown values etc. But move on at this point and get to a core issue.

You see, Groovy doesn’t really have a concept of final. For that matter, it doesn’t really have the concept of private either. Most of the time you can freely modify final fields, and access internal private members.

Go ahead, try it!

This is a very Python way of doing things. But we (Java devs) like control. We like safety and encapsulation. And when it come to immutable objects, we need it!

Sure you can, correctly do immutable objects in groovy:

@Immutable

class SimpleBean {

int value1

String value2

}

Simple enough. It even, somehow, enforces the final nature and private variables. Hazah!

Only not.

What if I want a custom constructor?

Sorry, you can’t provide a constructor for Immutable objects.

Ok, what about default values?

Nope, can’t do that either.

What about non-null enforcement?

Nope.

Basically, if you want any of that you need to create a builder for your immutable object and hope and pray someone doesn’t call that public constructor that is just sitting around, waiting for them.

Photo by David Bartus from Pexels

Java Modules

Groovy relies heavily on reflection internally. So much so that for a awhile after Java modules were released Groovy just won’t work. Even with reflection you can’t access stuff you shouldn’t anymore. To be fair, this broke lots of other libraries, slf4j, CORBA, Spring, Jenkins, etc. Anything that relied on a global classpath with unfettered access broke.

Now this was something I was actually used to, and welcomed, since it brought me back to my OSGi days of multiple classpaths. But I hadn’t expected Groovy to be so broken out of the gates.

Truthy-ness

You love it or you hate it. At first I loved it:

List items;

if (items) {

...

}

But lets dig into this. What is actually happening in the if clause. Groovy will try to convert items in a conditional clause to true. Its not always the same and not always apparent. In this case, if items is non null and non empty then the clause is true. Fairly benign. Lets try something else:

BigDecimal v1 = 0.00

BigDecimal v2 = 0.000 v1 == v2

Is the equals true or false here? So first off, if you didn’t know as Java devs, ‘==’ in groovy is not an equivalence check. Nor, as many docs would tell you, is it a shortcut to ‘equals’. No, groovy is sneaky. If it can it will try a ‘compareTo’ first then it will fall back on ‘equals’. So the statement above v1 does NOT equals v2 since the precision is different. But since groovy uses compareTo the ‘==’ will evaluate to true. Now you could argue that 0.00 is equal to 0.000 back in some cases we want to know if the precision is also different.

Which kind of gets to my point. It’s not obvious what Groovy is doing sometimes until it breaks in weird ways at runtime.

by Pixabay from Pexels

Compilation Tooling

groovyc is fine, as much as gcc is fine and javac is fine. Meaning its not fine …at all. I have struggled repeatedly with using Maven to compile groovy. Now, to be fair, this isn’t really groovy’s fault but more so Maven’s.

There are lots of ways to do it.

GMaven Plus doesn’t integrate into the normal maven compiler, and while it claims to support joint compilation (compiling groovy and java at same time) I found that it didn’t always work. Especially when there was some circular dependency between Java and Groovy.

Groovy Eclipse isn’t much better. It is very difficult to configure. You need to ensure you have the right version of the compiler and batch libraries for the version of groovy you are intending to use. It does a good job with joint compilation. But doesn’t do groovy docs, or groovy execution.

Gradle does seem to handle groovy a bit better than Maven does.

Other Tooling

As a secondary language Groovy gets secondary tooling. Checkstyle doesn’t work. Codenarc is kinda okay, Sonar sort of supports it. IDE’s mostly work correctly with it. Eclipse is terrible, Intellij works fairly well.

Noticing a trend?

Falling Behind Java

Being a language built on top of another language you will by definition always be lagging behind in features (java modules). Or if you try and innovate and move ahead you run the risk of feature conflict (closures and lambda). Both of these are common in Groovy as highlighted above.

Version Compatibility

Groovy does good things here and some not so good things here. I can commend them that I can take a piece of groovy code compiled for groovy 1.8 and still run it in 2.5 Great job!

But why do things break between minor version changes? Why do you change behavior? Why do you lie?!?

If you use Groovy, then Groovy release notes are almost required reading.

This is a common theme in Groovy. They change stuff. And they change stuff in ways that I would consider to be major changes but they don’t reflect that in their version numbers.

For example, from Groovy 2.4 to 2.5 they changed their CLI tooling. On the surface this doesn’t seem like a big deal, and all the code still compiled. But we had several tools that went from named command line arguments needing a long name with two dashes

--name <value>

To needing a single ‘-’.

-name <value>

This broke lots of our automated tooling and took forever for us to figure out.

by Pixabay from Pexels

Other weird shit

Did you know that Groovy prior to 2.4 inserted a field into every class that was the timestamp of compilation? I didn’t, until I did API analysis checks on our libraries and found every version of our classes was binary incompatible with previous releases.

Or that old versions of Groovy didn’t support calling a super method if you overrided it in a subclass.

Or that groovy doesn’t support many now standard Java syntax like:

default methods in interfaces (coming Groovy 3)

Java Try with resources block (coming Groovy 3)

Method references (coming Groovy 3)

Nested code blocks (coming Groovy 3)

This kind of this has become a meme at work. “Oh yeah that … #groovy”

Photo by rawpixel.com from Pexels

BLOAT

With every release of Groovy they add lots of stuff. At first this seems awesome. All these new ways to do something. But Groovy tries to be everything to everyone. It adds lots of baggage to the language. While the syntax may be shorter, the syntax is also more complex and always changing.

There is something to be said for keeping a language feature set small. Don’t have lots of ways to do something. Instead have a few ways to do it right. Take Go for example. It is by no means incredibly feature rich. Quite the opposite. But you can learn the syntax for it in an afternoon.

Besides syntax bloat there is library bloat as well. As library developers we need to be conscious of the dependencies we burden our consumers with.

Dependencies are not free.

Increased size in deployed applications

Increased risk of dependency collisions (Not in OSGi :P)

Now groovy has done different things in the past to solve these problems. By shadding their dependencies they eliminate the classpath collision problem but doubled code size. So they went back to reusing OSS libraries, but that reintroduced collisions.

by Pixabay from Pexels

The breakup

So here we stand. One time Java dev, ran off with Groovy. The new hot thing. Now I want Java back. Java 8 adds lots of great stuff, like streams which eliminate the need for Groovy closures. But how do I get lots of the code simplifing things I liked about groovy.

Lombok is a javac extension. It inserts code at compilation time. The code it inserts has ZERO dependencies on other libraries. The code is fast and follows good programming practices. Best of all it makes reading Java very simple.

Yes, you read that right JAVA! You write .java files and can write ANY java code in the class files you are using Lombok on.

Mutable Bean (Get/Set/Equals/HashCode/ToString)

import lombok.Data;



@Data

public class SimpleBean {

int value1;

String value2;

}

Immutable Bean:

import lombok.Value;



@Value

public class SimpleBean {

int value1;

String value2;

}

Immutable bean with builder pattern, default values, and non null enforcement:

@Value

@Builder

public class SimpleBean {

@Builder.Default

int value1=25;

@NonNull

String value2;

}

Where do I got from here.

Well

I still write Groovy, I write Java with Lombok

For me the one thing Groovy has hands down better than Java right now isn’t even really groovy.

by Pixabay from Pexels (CC0)

We all write tests. But Junit tests are annoying to write because we are stuck with the language features of Java. Spock is an awesome testing framework that uses the language features of Groovy for good not evil. I can write human readable tests quickly. I can mock, stub, spy, inject all quickly and easilly. I get great informative error messages when tests fail.

class SimpleBeanSpec extends Specification {

def "test simpleBean"() {

given:

SimpleBean bean = new SimpleBean()



when:

bean.value1 = 25



then:

bean.value1==25

}

}

Basically, if you aren’t writing your tests in Spock, you really should. Since I only use Spock for tests then all the groovy dependencies and many of the other problem I run into with groovy simply go away.

I miss…

I still miss many things of Groovy.

I miss the:

Ease of creating lists and maps

String interpolation

Null dereference

But I find I don’t miss them enough to warrant the continued struggles. I know Groovy 3 promises to fix many of the issues I brought up. But it also does a lot of the same things I don’t like. Groovy continues to introduce even more syntax. And Groovy is making other big changes, like changing the parser. Most of all they are still lagging behind Java.

Maybe someday, I’ll go back to Groovy as an old friend, or Java will finally add just the few things I feel it lacks. Or maybe, just maybe, something else might come along…

Kotlin anyone?