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
Advertisement
Answer
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 int
s instead of boxed Integer
s. 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; }