Making Java 8 Groovier: A few annotated examples

In a couple of weeks, I’m giving two talks at talk at the 2016 JavaOne conference in San Francisco. One of them is called “Groovy and Java 8: Making Java Better“. I’m building examples in preparation for the conference, and as the Groovy community is good about correcting my errors in a friendly way, I thought I’d show some of them here ahead of time.

[Note: the session is labeled CON3277 and will take place Monday, Sept 19, from 12:30 – 1:30pm in the Hilton Plaza Room A, according to the session catalog.]

[One more aside: my co-host on the Groovy Podcast, Baruch Sadogursky, is participating in “The Ultimate Build Tools Face-off“, representing Gradle against Maven and Bazel. The winner of the face-off is determined by audience applause, so if you are within 500 miles of the event, be sure to make your voice heard. If I wasn’t giving my talk at the exact same time, I’d definitely be there.

Let me put that another way. As T’Pau said in the Star Trek original series episode Amok Time:

T’Pau: If both survive the lirpa, combat will continue with the ahn woon.
Kirk: Ah, what do you mean “if both survive?”
T’Pau: This combat is to the death. (emphasis unnecessarily added)

to_the_death_1

So Baruch, if you’re reading this: DON’T SCREW IT UP.

But no pressure.]

Anyway, my (first) talk is a demonstration of how Groovy goes beyond the functional capabilities added in Java 8, but can be used from Java to make life easier. Of course I’m going to talk about Java 8 lambdas vs Groovy closures, and how the method references syntax is different, but in this post I thought I’d highlight a couple of the cool AST transformations that are somewhat less common.

First, there’s memoize. Memoization is the process of building a cache of method calls, so if the same call occurs again, the system can return the cached value right away rather than re-computing it.

One way Groovy accomplishes this is by adding a memoize method to the Closure class. To build the cache, simply invoke the method. For example, consider a variable called add assigned to a closure that takes two arguments and sleeps for one second before returning their sum.

def add = { x, y -> sleep 1000; x + y }.memoize()

println add(3, 4)      // takes 1 sec
println add(3, 4)      // immediate
println add('a','b')   // takes 1 sec
println add('a','b')   // immediate

Invoking the memoize method replaces the closure with a new one that has keeps a cache of method calls. Therefore, the first call to add with any arguments executes normally, but the second and all subsequent calls with the same arguments pulls the value out of the cache. Pretty sweet.

Recursive calls are naturals for this, and the classic example is computing Fibonacci numbers. Here are two separate ways to memoize a fib function. In the first, a variable is assigned to a closure, so internally the closure uses the call method for recursion.

def fib = { n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
}.memoize()

Alternatively, the @Memoize Abstract Syntax Tree (AST) transformation can be applied to a method to accomplish the same thing.

@Memoized
long fib(long n) {
    if (n < 2) 1
    else fib(n - 1) + fib(n - 2)
}

Either way, the result of each call with a particular value of n is saved, so the recursive calls return almost immediately.

To demonstrate that I can use that from Java, I put the method in a Groovy class.

import groovy.transform.Memoized

class AnnotatedFunctions {
    @Memoized
    BigInteger fib(BigInteger n) {
        if (n < 2) 1
        else fib(n - 1) + fib(n - 2)
    }

    @Memoized
    BigInteger fact(BigInteger n) {
        if (n < 2) 1
        else n * fact(n - 1)
    }
}

In addition to the Fibonacci method fib, I also have a recursive factorial computation, fact. In Java, it’s easy enough to instantiate the Groovy class and invoke its methods directly. Here’s a snippet from the main method of my Java class.

AnnotatedFunctions mf = new AnnotatedFunctions();

IntStream.range(1, 100)                
    .forEach(i -> {
        long before = System.nanoTime();
        BigInteger val = mf.fib(new BigInteger(i + ""));
        long after = System.nanoTime();
        System.out.printf("%3d: %8s, fib(%2d) = %d%n", i, 
            (after - before) / 1e9, i, val);
    });

IntStream.range(1, 100)
    .forEach(i -> {
        long before = System.nanoTime();
        BigInteger val = mf.fact(new BigInteger(i + ""));
        long after = System.nanoTime();
        System.out.printf("%3d: %8s, fact(%2d) = %d%n", i, 
            (after - before) / 1e9, i, val);
    });

The output resembles:


  1:  0.10599, fib( 1) = 1
  2:  0.02197, fib( 2) = 2
  3:   2.1E-4, fib( 3) = 3
  4:  1.56E-4, fib( 4) = 5
  5:  1.71E-4, fib( 5) = 8
  6:   1.7E-4, fib( 6) = 13
  // ...
 98:  1.19E-4, fib(98) = 218922995834555169026
 99:  1.16E-4, fib(99) = 354224848179261915075

  1:  2.74E-4, fact( 1) = 1
  2: 0.002715, fact( 2) = 2
  3:  1.34E-4, fact( 3) = 6
  4:  1.31E-4, fact( 4) = 24
  5:   8.1E-5, fact( 5) = 120
  6:  1.41E-4, fact( 6) = 720
  // ...
 98:   5.5E-5, fact(98) = 9426890448883247745626185743057242473809693764078951663494238777294707070023223798882976159207729119823605850588608460429412647567360000000000000000000000
 99:   5.3E-5, fact(99) = 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000

Pretty impressive, and trivial to do in Groovy even if you just want to call it from Java.

Another AST transform that comes up if the algorithm is right is @TailRecursive. If you can express your algorithm in a way that the recursive call comes last, you can use that transform, which will also check that you did it properly.

I added the following method to my AnnotatedFunctions class.

import groovy.transform.TailRecursive
class AnnotatedFunctions {
    // ... other methods ...

    @TailRecursive
    BigInteger factorial(BigInteger n, BigInteger acc = 1G) {
        n < 2 ? acc : factorial(n - 1G, n * acc)
    }
}

I don’t normally write BigInteger, since Groovy automatically uses it if the system requires that many digits, but when integrating with Java it helps to be explicit. I’m also taking advantage of Groovy’s ability to optionally initialize a method variable by assigning the acc accumulator the value of 1.

Now I can call this from Java, too.

AnnotatedFunctions mf = new AnnotatedFunctions();
// ... other calls from before ...

System.out.println("70000! has " +
     mf.factorial(new BigInteger(70_000 + "")).toString().length() +
     " digits");

[This also takes advantage of the fact that starting in Java 7, you can embed underscores in numeric literals, like 70_000, for readability.]

The output is: 70000! has 308760 digits.

Another cool AST transform is @Immutable. Functional programming favors immutability, but it’s enormously difficult to make a Java class produce immutable objects. You have to remove all the setters, provide private final backing fields for properties, wrap collections in their unmodifiable equivalents, make the class final, and so on.

Or, you can just use the @Immutable annotation, which does all that and more for you. Here’s an immutable point class.

import groovy.transform.Immutable

@Immutable
class Point {
    double x
    double y
}

Here is a Spock test (aside — my other JavaOne talk is on Spock testing) that demonstrates its capabilities.

import spock.lang.Specification

class PointSpec extends Specification {
    def 'tuple constructor works'() {
        expect: new Point(3, 4)
    }

    def "can't change x"() {
        given:
        Point p = new Point(1, 2)

        when:
        p.x = 5

        then:
        thrown(ReadOnlyPropertyException)
    }

    def "can't change y"() {
        given:
        Point p = new Point(1, 2)

        when:
        p.y = 5

        then:
        thrown(ReadOnlyPropertyException)
    }
}

I couldn’t really leave that alone, so I added a few more methods.

import groovy.transform.Immutable

@Immutable
class Point {
    double x
    double y


    static Point createPoint(double x, double y) {
        new Point(x, y)
    }

    Point translate(double dx = 0, double dy = 0) {
        new Point(x + dx, y + dy)
    }

    Point rotate(double radians) {
        double r = Math.sqrt(x * x + y + y)
        new Point(r * Math.cos(radians), r * Math.sin(radians))
    }

    Point plus(Point p) {
        new Point(x + p.x, y + p.y)
    }

    Point minus(Point p) {
        new Point(x - p.x, y - p.y)
    }
}

The translate and rotate methods produce new points that are the result of moving or rotating the original point. I also added a plus and a minus method to take advantage of operator overloading. The corresponding tests are:

import spock.lang.Specification

class PointSpec extends Specification {
    // ... other tests ...

    def "can translate"() {
        given:
        Point start = new Point(1, 0)
        Point end = new Point(3, 3)

        when:
        Point p = start.translate(2, 3)

        then:
        assert (p.x - end.x).abs() < 1e-10
        assert (p.y - end.y).abs() < 1e-10

    }

    def "can rotate 90 deg"() {
        given:
        Point p = new Point(1, 0)

        when:
        p = p.rotate(Math.PI / 2)

        then:
        p.x.abs() < 1e-10
        p.y == 1
    }

    def "can rotate 180 deg"() {
        given:
        Point p = new Point(1, 0)

        when:
        p = p.rotate(Math.PI)

        then:
        p.x == -1
        p.y.abs() < 1e-10
    }

    def "overloaded plus"() {
        given:
        Point p1 = new Point(1, 2)
        Point p2 = new Point(3, 4)

        when:
        Point p = p1 + p2

        then:
        p.x == 4
        p.y == 6
    }

    def "overloaded minus"() {
        given:
        Point p1 = new Point(1, 2)
        Point p2 = new Point(3, 4)

        when:
        Point p = p1 - p2

        then:
        p.x == -2
        p.y == -2
    }
}

The only nuisance is that because I’m planning to integrate with Java, I’m using doubles rather than BigDecimals, and that means the precision of zero isn’t quite what I need. Everything works, though, including the tuple constructor.

That’s significant, because I ran into a problem when trying to call this from Java.

public class UsePoint {
    public static void main(String[] args) {
        // Point p = new Point(1, 0) // doesn't work (aww)

        Point p = Point.createPoint(1, 0);
        System.out.println(p);
        System.out.printf("(%s,%s)%n", p.getX(), p.getY());

        Point p1 = p.translate(2, 3);
        System.out.println(p1); // should be (3,3)

        Point p2 = p.rotate(Math.PI / 2);
        System.out.println(p2); // should be (0,1)
    }
}

Even though the AST transform generates a tuple constructor for Point, the Java code apparently is compiled too soon to see it. I was forced to add a createPoint method to Point in order to instantiate the class.

The rest works, though. I can invoke translate or rotate without a problem. The plus and minus methods don’t help Java much, since they’re there just for the Groovy operator overloading. Of course, there are no setters (no setX or setY methods) available, so I don’t have an issue with Java trying to call them.

I’m going to talk about streams, lambdas, and method references, too, but this post has enough in it for now. I’ll show that stuff in my next post. Besides, that’ll give me another chance to “encourage” Baruch in the Thunderdome.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

2 Responses to Making Java 8 Groovier: A few annotated examples

  1. Pingback: Diario di Grails (settimana 35 e 36 del 2016) | BME

  2. Pingback: Java Annotated Monthly – October 2016 | IntelliJ IDEA Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: