13

I just wanted to define a Java 8 lambda expression recursively.

The Lambda FAQ mentions that one can define a recursive lambda expression only during (static) field initialization.

But I get a compiler error in IntelliJ (javac just reports an error without a message):

java: self-reference in initializer

If I try to write something like:

static UnaryOperator<Integer> f = i -> i == 0 ? 1 : i * f.apply( i - 1);

or

UnaryOperator<Integer> f = i -> i == 0 ? 1 : i * f.apply( i - 1);

One way I found to make it work was to use an array for referencing the lambda effectively tricks the java compiler:

import java.util.function.UnaryOperator;

public class RecursiveLambdaExample {

    public static void main(String[] args) {

        UnaryOperator<Integer>[] fac = new UnaryOperator[1];
        fac[0] = i -> i == 0 ? 1 : i * fac[0].apply( i - 1);

        UnaryOperator<Integer> factorial = fac[0];

        System.out.println(factorial.apply(5));
    }
}

Is there another trick to define recursive lambda expression?

Thomas Darimont
  • 1,356
  • 11
  • 14
  • 3
    you REALLY should'nt mix lambda with recursion - doing so pretty much assures major, unexpected or even unexplicable problems. Mixing these things is like mixing two VERY volatile chemicals, the resulting explosion will literally destroy your free time in no time at all. – specializt Aug 11 '14 at 21:52
  • 1
    I know ;-) just wanted to see whats possible :) – Thomas Darimont Aug 11 '14 at 21:57
  • see my answer for the problem. You can use a 'this' variable to tackle this. https://stackoverflow.com/questions/19429667/implement-recursive-lambda-function-using-java-8/55353292#55353292 – Arundev Oct 17 '19 at 09:49

3 Answers3

12

You can make it work by fully-qualifying the field name that you're referencing recursively. This version compiles without any error:

import java.util.function.UnaryOperator;

public class RecursiveLambdaExample {

    static UnaryOperator<Integer> fac = i -> i == 0 ? 1 : i * RecursiveLambdaExample.fac.apply( i - 1);

    UnaryOperator<Integer> f = i -> i == 0 ? 1 : i * this.f.apply( i - 1);

    public static void main(String[] args) {
        System.out.println(new RecursiveLambdaExample().f.apply(5));
        System.out.println(fac.apply(5));
    }
}

Related: Why do lambdas in Java 8 disallow forward reference to member variables where anonymous classes don't?

Community
  • 1
  • 1
DaoWen
  • 32,589
  • 6
  • 74
  • 101
  • Ah yes, this works - thanks! UnaryOperator f = i -> i == 0 ? 1 : i * this.f.apply( i - 1); seems to be enough already for the instance field case. – Thomas Darimont Aug 11 '14 at 22:15
  • @ThomasDarimont - Good to know! I assumed that `this` inside a lambda would refer to the lambda itself and thus cause problems, so I decided to go with `RecursiveLambdaExample.this` instead. I'll edit my answer. – DaoWen Aug 11 '14 at 22:28
  • @DaoWen: if there was a way to refer to the lambda itself, that enabled invoking it recursively without needing to access the outer variable where the lambda is stored. But lambdas cannot refer to itself, `this` and `super` have the meaning of the outer scope and a lambda can not invoke any method on the instance created for the lambda expression. – Holger Aug 12 '14 at 11:06
2

You are able to achieve this with nested class:

public class Main {

    public static void main(String[] args) {

        class Helper {
            final UnaryOperator<Integer> f = i -> i == 0 ? 1 : i * this.f.apply( i - 1);
        }

       System.out.println(new Helper().f.apply(3));
    }
}

output:

6
Matthew I.
  • 1,793
  • 2
  • 10
  • 21
0

just expand your lambda to an anonymous class:

    UnaryOperator<Integer> f = new UnaryOperator<Integer>(){
        @Override
        public Integer apply(Integer i) {
            return i == 0 ? 1 : i * this.apply( i - 1);
        }
    };
dizi
  • 1