Anonymous class replaced with lambda expressions

10.7k views Asked by At

I have sample code which uses Java 8 new stream functionality (get a range of int values 1 .. 20, skip the first 9, then take remaining 10, each int value: reduce by one and multiply by 2).

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand * 2;
        }
    })).toArray()));

The output is as follows:

[18, 20, 22, 24, 26, 28, 30, 32, 34, 36]

Now I want to replace anonymous classes with Lambda expressions. The following conversion works fine (the second anonymous class replaced with i -> i * 2 lambda expression) and I get the same output:

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(i -> i * 2)).toArray()));

However, when I replace the first anonymous class with lambda expression:

System.out.println(
    Arrays.toString(
        IntStream.rangeClosed(1, 20).skip(9).limit(10)
            .map((v -> v - 1).andThen(i -> i * 2)).toArray()));

I am not able to compile my code. The error says Operator '-' cannot be applied to '<lambda parameter>', 'int'

Compilation error

Do you know how to combine two lambda expressions with IntUnaryOperator.andThen?

I know I could use ... .limit(10).map(v -> v - 1).map(i -> i * 2).toArray() ... which works fine but I would like to find out how to use IntUnaryOperator.andThen with lambdas (if that possible).

3

There are 3 answers

1
jpvee On BEST ANSWER

You have to explicitly cast the first lambda expression to IntUnaryOperator. The following code works:

System.out.println(
        Arrays.toString(
                IntStream.rangeClosed(1, 20).skip(9).limit(10)
                        .map(((IntUnaryOperator) v -> v - 1).andThen(i -> i * 2)).toArray()));
0
nobeh On

I managed to compile the code as the following (although using eclipse experimental versions):

IntUnaryOperator first = v -> v - 1;
IntUnaryOperator second = i -> i * 2;
System.out.println(Arrays.toString(IntStream.rangeClosed(1, 20).skip(9)
                .limit(10).map(first.andThen(second)).toArray()));

I understand your interest to be able to write the code without declaring first and second; however, IntUnaryOperator is a FunctionalInterface and based on how Java compiler works, a FunctionalInterface needs to have a "context" to be able to be composed the way you want it. This is why with your original code snippet Java compiler does not have a way to map your lamda expression to exactly an instance of IntUnaryOperator.

For a longer discussion take a look at this thread: http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-June/002668.html

1
isnot2bad On

Another solution is to use the static IntUnaryOperator.identity() as a startpoint:

IntUnaryOperator.identity().andThen(v -> v - 1).andThen(i -> i * 2)

With static imports you can use just identity() too!

If IntUnaryOperator had a static method like...

static IntUnaryOperator first(IntUnaryOperator op) { return op; }

... we could simply write:

IntUnaryOperator.first(v -> v - 1).andThen(i -> i * 2)