How can a Java lambda-expression reference itself?

2020-05-24 04:25发布

问题:

I found this article to be very informative in comparison of old-style functions to new Java-8 lambda-functions and parallel processing. One thing I couldn't quite understand was one restriction on the lambda-functions: From page 4:

3.3 Preconditions Although lambda expressions are intended as a more con- cise alternative to AIC , they are not a complete replacement. There are several preconditions that LambdaFicator checks before refactoring an AIC into a lambda expression. These preconditions are inherent to how lambda expressions are implemented in Java, not limitations of our tool. (P1) AIC must instantiate from an interface. Instances of abstract or concrete classes cannot be converted to lambda expressions. (P2) AIC must have no fields, and declare only one method. A lambda expression represents a single anonymous func- tion; therefore, an AIC with multiple methods can not be converted to a single lambda expression. (P3) AIC must not have references to this or super . In a lambda expression, this and super are lexically scoped, meaning they are interpreted just as they would be in the enclosing environment, e.g., as if they appeared in the state- ment before the lambda expression [6]. However, in an AIC they refer to the inner class itself. (P4) AIC must not declare a recursive method. In order to perform the recursive call, we must obtain a reference to the anonymous function. While LambdaFicator could perform this refactoring, this could introduce unneeded complexity into the code and harm understandability.

On P4, "AIC must not declare a recursive method... LambdaFicator could perform this refactoring...", how could one refactor a lambda expression to reference itself? Since by definition these lambda anonymous-functions don't have a name that can be referenced, and don't have a reference to themselves (P3 above).?

回答1:

public class Test {
    static Runnable r;

    public static void main(String... args) {
        r = () -> r.run();
        r.run();
    }
}

The Runnable obtains a reference to itself from the field r when it is run.

You could also use a length 1 array to store the reference if you don't like adding a field.



回答2:

As said here, Java’s canonical way to implement a recursive function is a method:

public static int fib(int n) {
    return n==0? 0: n==1? 1: fib(n-1)+fib(n-2);
}

Then, if you need a instance fulfilling a functional interface you can use a method reference:

Function<Integer, Integer> fib = MyClass::fib;

or

IntUnaryOperator fib0=MyClass::fib;

This is the closest equivalent to a lambda expression as a lambda expression is not just syntactic sugar for a runtime generated class replacing the anonymous inner class but also for an anonymous method hosting the code of the single abstract method to implement.

Using an ordinary recursive method turns the anonymous method into a named one while maintaining all other properties of lambda expressions. This differs from all other workarounds trying to give a lambda expression a reference to itself, like storing the instance into a field. These workarounds are not semantically equivalent (and less efficient).



回答3:

Derived from @Alex's answer:

@FunctionalInterface
public interface SelfRunnable extends Runnable {
  public void run(SelfRunnable this_);

  @Override
  public default void run() {
    run(this);
  }

  public static Runnable runnable(SelfRunnable runnable) {
    return runnable;
  }
}

public interface Test {
  public static void main(String... arguments) {
    final Runnable r = SelfRunnable.runnable(this_ -> this_.run());
    r.run();
  }
}


标签: lambda java-8