March 8, 2015

10 most useful Groovy features (part 1 of 2)

I dedicate my first blog post ever to the programming language I do like the most: Groovy. Here, I’d like to share what in my opinion are my top 10 most useful features that JVM language offers (which Java doesn’t offer).

This is not an easy choice. Groovy just comes with too many little “syntactic sugar” additions as well as full-blown features which turn many annoying, bloated Java development tasks really into a breeze, increasing ease of development and maintainability by drastically decreasing the amount of boilerplate code. I found it easier to come up with this list if I asked myself: if, for whatever reason, Groovy wouldn’t be there anymore, which of its features would I miss most?

I actually could as well ask: which Groovy features do I miss most in non-Groovy (Java) projects? But in that situation I typically try to avoid this questions as it would naturally lead to immediate frustration… So let’s think in Groovy, again – here goes my list:

1. Collection literals

In Groovy:
public static List<?> getList() {
    return [1, 2, 3, [4, 5]]
}

public static Map<String, Integer> getMap() {
    return [
        John: 30,
        Alice: 40,
        Mike: 50,
    ]
}
In Java:
public static List<?> getList2() {
    return new ArrayList<>(Arrays.asList(1, 2, 3, 
            new ArrayList<>(Arrays.asList(4, 5)))
    );
}

public static Map<String, Integer> getMap() {
    Map<String, Integer> ret = new HashMap<>();
    ret.put("John", 30);
    ret.put("Alice", 40);
    ret.put("Mike", 50);
    return ret;
}
First and foremost, this is literally (pun intended!) the feature I miss most in non-Groovy projects. Collections are just everywhere! In Java, they are very unpleasant and cumbersome to work with. With Groovy’s literal syntax however, initializing a collection is very natural and highly readable. Of course, this also works when statically compiled, and therefore, I sincerely hope to see this syntactic enhancement in Java one day!

Note that whilst seriously simplifying a simple ArrayList initialization, Groovy especially makes initialization of LinkedHashMaps far easier and far more readable than equivalent Java code. To a much broader extend, this is true for nested collection structures as well.

2. GroovyBeans properties

In Groovy:
private static class Person {
    String name
    int age
    boolean active
    Person father
}

public static List<?> getPropertiesList() {
    Person person = new Person(name: "Tim")
    person.age = 20
    person.active = true
    
    // set grandfather
    person.father = new Person(name: "Tim's father")
    person.father.father = new Person(name: "Tim's grandfather")
    
    return [person.name, person.age, person.active, person.father.father.name]
}
In Java:
private static class Person {
    private String name;
    private int age;
    private boolean active;
    private Person father;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public Person getFather() {
        return father;
    }

    public void setFather(Person father) {
        this.father = father;
    }
}

public static List<?> getPropertiesList() {
    Person person = new Person();
    person.setName("Tim");
    person.setAge(20);
    person.setActive(true);
            
    // set grandfather
    person.setFather(new Person());
    person.getFather().setName("Tim's father");
    person.getFather().setFather(new Person());
    person.getFather().getFather().setName("Tim's grandfather");
    
    return new ArrayList<>(Arrays.asList(
        person.getName(), person.getAge(), person.isActive(),
        person.getFather().getFather().getName()
    ));
}
Whenever you need a more sophisticated data structure than a simple collection, you’re using an object as a container. But even though in Java, everything resolves around object access, and even though Java denotes the concept of a JavaBean as a structure which encapsulates properties, the language still lacks direct syntactic support for both declaring and accessing a bean’s properties, a feature present in many other wide-spread programming languages. For instance, C# knows the concept of accessors which implement getter / setter access to properties.

In Java, we still have to manually write getters / setters (even though we typically generate them in the IDE, including all drawbacks of generated code), and the syntax to access properties is still cumbersome.

Groovy, on the other hand, addresses both issues:
  • Declaring a field as a property is trivial: just omit the visibility modificator, and getters / setters will be weaved into bytecode;
  • Access is trivial: just use “GPath” style dot-notion to access properties (which works for all Groovy and Java classes “JavaBean” compliant properties).
Additionally, Groovy offers an extra map-constructor for all properties of a class.

Note how, in the example, access for all properties in Groovy is unified (including boolean properties), and how particularly fluent and highly maintainable code for accessing nested properties is. Also note that thanks to these enhancements, even in this simple example, the number of Groovy code lines is less than half the amount of Java code lines!

3. Closures

In Groovy:
public static List<String> collect() {
    return INPUT_LIST.collect {it.toUpperCase()}
}

public static List<String> collectMultiStatement() {
    return INPUT_LIST.collect {String ret = it.toUpperCase(); ret}
}
In Java:
public static List<String> collect() {
    return INPUT_LIST.stream().map(it -> it.toUpperCase())
            .collect(Collectors.toList());
}

public static List<String> collectMultiStatement() {
    // Mind the brackets, the return keyword and the conclusive semicolon!
    return INPUT_LIST.stream().map(it -> {String ret = it.toUpperCase(); return ret;})
            .collect(Collectors.toList());
}
In Java 8, the language proudly introduced the so-called “lambda expressions”, which are basically method references, enabling a more functional programming style.

This feature actually has been present in Groovy ever since its earliest versions, where it’s known as “closures”, thus being interweaved closely into the Groovy JDK (“the GDK”), especially in the Collections framework.

As a matter of fact now, Groovy closure syntax is still much more concise and intuitive than its “newer” Java 8 lambda counterpart. Yet, most of what makes closures easier to read and write than lambdas works also in statically compiled mode. I would even claim that writing code in Groovy closures makes it easier to read than using procedural loop instructions, whilst in Java, using lambdas would actually make it harder to read.

For instance, observe how even in this basic (although artificial) examples, the Groovy syntax is much shorter and simper.

Meanwhile I’ve also posted an in-depth comparison between closures and lambdas.

4. GDK enhancements

In Groovy:
public static String getAt() {
    INPUT_LIST[-1] = "Z"
    return INPUT_LIST[-1]
}

public static List<Integer> flattenList() {
    return INPUT_NESTED_LIST.flatten() as List<Integer>
}
In Java:
public static String getAt() {
    INPUT_LIST.remove(INPUT_LIST.size()-1);
    INPUT_LIST.add("Z");
    return INPUT_LIST.get(INPUT_LIST.size()-1);
}

public static List<Integer> flattenList() {
    return flattenList(INPUT_NESTED_LIST);
}

private static List<Integer> flattenList(List<?> list) {
    List<Integer> ret = new ArrayList<>();
    for (Object elem : list) {
        if (elem instanceof List) {
            ret.addAll(flattenList((List<?>) elem));
        }
        else {
            ret.add((Integer) elem);
        }
    }
    return ret;
}
Having covered, so far, these omnipresent syntactic simplifications Groovy brings in, I would judge the numerous enhancements Groovy provides for most JDK classes the next big advantage. Groovy introduces many additional methods as well as first-class operators for many of the classes in the JDK which is hence called the “GDK” instead.

These enhancements serve both simplification and unification of existing functionality. Several special operators are introduced for a more concise application of existing method calls, and especially for the collection classes, for String and File class, and for many more, a great amount of functionality has been implemented in a single method call which you would otherwise have to implement by hand (typically in a rather anti-pattern static “util class” style) or introduce non-standardized 3rd party libraries.

Many of these additional methods do of course work seamlessly with Groovy closures, but there are also numerous simple util methods as well.

5. Dynamic typing

In Groovy:
private static final List<?> INVENTORY = [
    new Car(), new Car(),
    new TV(), new TV(), new TV()
]

private static class Car {
    public final int cost = 10000
}

private static class TV {
    public final int cost = 1000
}

@CompileDynamic
public static int sumDynamic() {
    return INVENTORY.sum { it.cost } as int
}
In Java:
private static final List<?> INVENTORY = new ArrayList<>(Arrays.asList(
    new Car(), new Car(),
    new TV(), new TV(), new TV()
));

private static class Car {
    public final int cost = 10000;
}

private static class TV {
    public final int cost = 1000;
}

public static int sumDynamic() {        
    return INVENTORY.stream().mapToInt(it -> {
        if (it instanceof Car) {
            return ((Car)it).cost;
        }
        else if (it instanceof TV) {
            return ((TV)it).cost;
        }
        else {
            throw new IllegalArgumentException("Unexpected type: " + it);
        }
    }).sum();
}
Whilst completely optional since Groovy 2.0, dynamic typing can greatly help you reducing boilerplate code as you can simply omit type specifications, if you so whish. Some Groovy developers will even advocate type-free programming per default whilst others would encourage you to stay within type safety as long as its advantages justify some more boilerplate code.

A typical use case of dynamic typing is presented in the example: Pure data-holder objects, that can for technical reasons not be embedded in a common inheritance hierarchy, with identically named properties need to be processed. In Java, you are forced to check and cast an object even if you know that a given method is present; in Groovy, due to its dynamic type system known as “duck typing”, you are free to call any method on any object. Groovy developers will in this situation assure program robustness through extensive UnitTests.

If you still have a bad feeling about leaving the security of compile-time type checking, just think of it as an additional “emergency” tool you have in your pocket for exactly those situations, as illustrated by the example. By way of comparison, the Fantom JVM language, originally strictly statically typed, features a “dynamic invoke” operator to invoke functions dynamically for “when a static type system gets in the way of an elegant solution”. This, of course, is the very opposite approach of the one that Groovy takes, for the latter is dynamic by default. Still, it proves that one point: In Groovy (and in Fantom) you have the opportunity to override static typing if this is useful for your specific situation. In other JVM languages such as Java or Scala, you simply cannot do this. It has been decided by the language specification that static typing is the only right solution for any situation.

Head on to page 2!

Pages: 1 2

No comments: