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
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With