2016-11-16 5 views
1

Я не знаю, возможно ли следующее. Я хотел бы run() метод а Runnable «S содержит сам Runnable, т.е.Реализация рекурсивной лямбда-функции в Java

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(); 
     //... 
    } 
}; 

ли что-то подобное возможно? Если да, то как? (CmdRun 's constructor is CmdRun(String command, Runnable executeAfter))

ответ

2

Является ли лямбда a обязательным здесь? Если нет, то переход на старшем эквивалентный синтаксис должен быть простым:

Пример:

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(); 
    } 
} 
+0

Это работает. Большое спасибо. – dotwin

+0

, хотя он работает, но рекурсивно выполняет что-то вроде повтора/повторного подключения и т. Д., И появление нового потока на каждом уровне рекурсии кажется склонным к ошибкам. Рассмотрите возможность изменения дизайна –

1

Самый простой способ, вероятно, разместить содержание вашей лямбды в методе и использовать ссылку метода для определения вашего Runnable.

1

Runnable run() не может содержать самостоятельную ссылку как незаконную. Im не совсем уверен, что вы пытаетесь достичь, но что-то, как это должно работать:

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(); 
    } 
} 
1

Короткий ответ: No.

Длинный ответ: Ваш код даст вам ошибку синтаксиса , Зачем? executeAfter, используемый внутри лямбда, не инициализируется; он инициализируется только после полного определения лямбда.

Например, рассмотрим приведенный ниже пример.

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

Ваш случай аналогичен. Внутри лямбда executeAfter не инициализируется. Как указано выше, он инициализируется только после полного определения лямбда.

Еще одна вещь для узла заключается в том, что переменная reconnects должна быть окончательной для использования внутри лямбда. Если это конечная переменная, вы не можете использовать оператор -- внутри этого условия.

1

На самом деле, если вы не возражаете, чтобы ввести новый интерфейс (или, если вам требуется такая функциональность более часто), вам можно использовать следующее:

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

Это теперь позволяет рекурсивно вызвать работоспособным, например:

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); 
    } 
}; 
Смежные вопросы