dotwin
dotwin

Reputation: 1332

Implementing a recursive lambda function in Java

I don't know if the following thing is possible. I would like a Runnable's run() method to contain the Runnable itself, i.e.

reconnects = 3;
Runnable executeAfter = () -> {
    if ( --reconnects < 0 ) {
        println("%nStop using port %d.", this.port);
        //...
    } else { // try to reconnect
        println("%nReconnecting...");
        cmdRun = new CmdRun(command, executeAfter);
        (new Thread(cmdRun)).start();
        //...
    }
};

Is something like this even possible? If so, how? (CmdRun's constructor is CmdRun(String command, Runnable executeAfter))

Upvotes: 1

Views: 775

Answers (5)

Roland
Roland

Reputation: 23252

Actually if you don't mind to introduce a new interface (or if you require such functionality more often), you could use the following:

@FunctionalInterface
interface RecursiveRunnable extends Runnable {
    default void run() {
        run(this);
    }
    public void run(RecursiveRunnable runnable);
}

This will now allow you to recursively call the runnable, e.g.:

int maxTries = 3;
AtomicInteger counter = new AtomicInteger();
RecursiveRunnable foo = runnable -> {
    if (counter.getAndIncrement() < maxTries) {
        println("Reconnecting... %n");
        runnable.run(); // same as: runnable.run(runnable)
    } else {
        println("Stop using port %d%n", port);
    }
};

Upvotes: 1

Adrian Shum
Adrian Shum

Reputation: 40036

Is lambda a must here? If not, switching to older equivalent syntax should be simple:

An example:

public class TestLambda {
    static int count = 0;
    public static void main(String[] args) {
        // lambda not going to work
        //Runnable foo = () -> { if (count < 5) { call(foo); } };
        // nor
        //Runnable foo = () -> { if (count < 5) { call(this); } };

        // using old way of anonymous inner class will work
        Runnable foo = new Runnable() {
            @Override public void run() {
                if (count < 5) {
                    call(this);
                }
            }
        };

        foo.run();
    }

    public static void call(Runnable action) {
        count++;
        System.out.println("in call " + count);
        action.run();
    }
}

Upvotes: 2

4runPr454th
4runPr454th

Reputation: 25

Short answer: No.

Long answer: Your code will give you a syntax error. Why? The executeAfter used inside the lambda is not initialized; it is only initialized after the full body of the lambda definition.

For example, consider the below example.

int i;
sum(i, 5); // Syntax error!! Variable i is not initialized...

Your case is similar. Inside the lambda, executeAfter is not initialized. As stated above, it is only initialized after the full body of the lambda's definition.

One additional thing to node is that the variable reconnects must be a final in order to be used inside the lambda. If it is a final variable, then you cannot use the -- operator on it inside your if condition.

Upvotes: 1

Dexter
Dexter

Reputation: 1750

The Runnable's run() can not contain a self reference as its illegal. Im not exactly sure what you are trying to achieve but something like this should work :

class CmdRun implements Runnable {

    final Object command;
    final Runnable runnable;

    final Runnable executeAfter = () -> {
        if ( --reconnects < 0 ) {
            System.out.println("%nStop using port %d." + port);
            //...
        } else { // try to reconnect
            System.out.println("%nReconnecting...");
            CmdRun cmdRun = new CmdRun(command);
            (new Thread(cmdRun)).start();
            //...
        }
    };

    public CmdRun(Object command) {
        this.command = command;
        this.runnable = executeAfter;
    }

    @Override
    public void run() {
        runnable.run();
    }
}

Upvotes: 1

assylias
assylias

Reputation: 328598

The easiest way is probably to put the content of your lambda in a method and use a method reference to define your Runnable.

Upvotes: 1

Related Questions