Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accumulator Generator test - Java 8

Tags:

java

java-8

Paul Graham, in his great article Revenge of the Nerds, claimed that languages vary in power. He mentioned a nice exercise - writing an accumulator generator:

We want to write a function that generates accumulators-- a function that takes a number n, and returns a function that takes another number i and returns n incremented by i.

Solution in Java is

public class Accumulator {

    public interface Inttoint {
        public int call(int i);
    }

    public static Inttoint foo(final int n) {
        return new Inttoint() {
            int s = n;
            public int call(int i) {
                s = s + i;
                return s;
            }};
    }

    public static void main(String... args) {
        Inttoint accumulator = foo(1);

        System.out.println(accumulator.call(2) == 3);
        System.out.println(accumulator.call(3) == 6);
    }

}

I am curious, whether in Java 8 (thanks to lambda) is already some elegant way how to write it similarly to Groovy, see below. I tried Function<Integer, Integer>

But I stuck with this compiler error.

local variables referenced from a lambda expression must be final or effectively final

So do you have some Java 8 solution?

Compare the old Java solution with the Groovy one

def foo(n) {
    return {n += it}
}

def accumulator = foo(1)
assert accumulator(2) == 3
assert accumulator(3) == 6
like image 702
banterCZ Avatar asked Jun 06 '14 13:06

banterCZ


1 Answers

First of all, you can still use all the new Java 8 interfaces using anonymous class syntax instead of lambda syntax. For example:

import java.util.function.IntUnaryOperator;

public class Accumulator {
    public static IntUnaryOperator foo(int n) {
        return new IntUnaryOperator() {
            private int value = n;
            @Override
            public int applyAsInt(int i) {
                return value += i;
            }
        };
    }

    public static void main(String... args) {
        IntUnaryOperator accumulator = foo(1);
        System.out.println(accumulator.applyAsInt(2)); // output: 3
        System.out.println(accumulator.applyAsInt(3)); // output: 6
    }
}

(Rather than Function, I used IntUnaryOperator here as it allows the use of primitive ints instead of boxed Integers. It's logically equivalent to Function<int,int>, if that were legal.)

Now, how can we shorten this bulky thing with lambda syntax? Local variables passed into lambdas are required to be (effectively) final. The limitation means you cannot trivially write a variable whose value accumulates between calls. The following does not work:

public static IntUnaryOperator foo(int n) {
    return i -> n += i; // nope, sorry!
}

We can work around the limitation by using some mutable object as a holder for the current accumulator value. A one-element array can be used for this. The array variable is not changing – only the contents of the array object it points at are changing, so the array variable is effectively final and this is allowed:

public static IntUnaryOperator foo(int n) {
    int[] value = new int[] { n };
    return i -> value[0] += i;
}

Any object with a mutable field can potentially be used as a holder. As suggested below by @andersschuller, an AtomicInteger fits well here, and makes the returned functions thread-safe:

public static IntUnaryOperator foo(int n) {
    AtomicInteger value = new AtomicInteger(n);
    return i -> value.addAndGet(i);
}

And @srborlongan points out this can be re-written using a method reference, which is even shorter (though not more readable):

public static IntUnaryOperator foo(int n) {
    return new AtomicInteger(n)::addAndGet;
}
like image 169
Boann Avatar answered Oct 13 '22 21:10

Boann