Java 8 лямбда-аргумент Void

Скажем, у меня есть следующий функциональный интерфейс в Java 8:

interface Action<T, U> {
   U execute(T t);
}

И для некоторых случаев мне нужно действие без аргументов или возвращаемого типа. Поэтому я пишу что-то вроде этого:

Action<Void, Void> a = () -> { System.out.println("Do nothing!"); };

Однако, это дает мне ошибку компиляции, мне нужно записать ее как

Action<Void, Void> a = (Void v) -> { System.out.println("Do nothing!"); return null;};

Что является уродливым. Есть ли способ избавиться от параметра типа Void?

Ответ 1

Синтаксис, который вам нужен, возможен с небольшой вспомогательной функцией, которая преобразует Runnable в Action<Void, Void> (вы можете поместить ее в Action например):

public static Action<Void, Void> action(Runnable runnable) {
    return (v) -> {
        runnable.run();
        return null;
    };
}

// Somewhere else in your code
 Action<Void, Void> action = action(() -> System.out.println("foo"));

Ответ 2

Используйте Supplier если он ничего не берет, но что-то возвращает.

Используйте Consumer если он что-то берет, но ничего не возвращает.

Используйте Callable если он возвращает результат и может выбросить (больше всего похоже на Thunk в общих терминах CS).

Используйте Runnable если он не делает ни одного и не может бросить.

Ответ 3

Лямбда:

() -> { System.out.println("Do nothing!"); };

фактически представляет реализацию для интерфейса, например:

public interface Something {
    void action();
}

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

Поскольку вы не можете расширить свой @FunctionalInterface, а также не ввести совершенно новый, то я думаю, что у вас не так много вариантов. Вы можете использовать интерфейсы Optional<T> для обозначения того, что некоторые из значений (тип возврата или параметр метода) отсутствуют. Однако это не упростит лямбда-тело.

Ответ 4

Вы можете создать суб-интерфейс для этого специального случая:

interface Command extends Action<Void, Void> {
  default Void execute(Void v) {
    execute();
    return null;
  }
  void execute();
}

Он использует метод

Ответ 5

Это невозможно. Функция, которая имеет непустой тип возврата (даже если она Void) должна вернуть значение. Однако вы можете добавить статические методы в Action, что позволит вам "создать" Action:

interface Action<T, U> {
   U execute(T t);

   public static Action<Void, Void> create(Runnable r) {
       return (t) -> {r.run(); return null;};
   }

   public static <T, U> Action<T, U> create(Action<T, U> action) {
       return action;
   } 
}

Это позволит вам написать следующее:

// create action from Runnable
Action.create(()-> System.out.println("Hello World")).execute(null);
// create normal action
System.out.println(Action.create((Integer i) -> "number: " + i).execute(100));

Ответ 6

Добавить статический метод внутри вашего функционального интерфейса

package example;

interface Action<T, U> {
       U execute(T t);
       static  Action<Void,Void> invoke(Runnable runnable){
           return (v) -> {
               runnable.run();
                return null;
            };         
       }
    }

public class Lambda {


    public static void main(String[] args) {

        Action<Void, Void> a = Action.invoke(() -> System.out.println("Do nothing!"));
        Void t = null;
        a.execute(t);
    }

}

Выход

Do nothing!

Ответ 7

Я не думаю, что это возможно, потому что определения функций не совпадают в вашем примере.

Ваше выражение лямбда оценивается точно как

void action() { }

тогда как ваша декларация выглядит как

Void action(Void v) {
    //must return Void type.
}

в качестве примера, если у вас есть следующий интерфейс

public interface VoidInterface {
    public Void action(Void v);
}

единственный вид функции (при создании экземпляра), который будет совместим, выглядит как

new VoidInterface() {
    public Void action(Void v) {
        //do something
        return v;
    }
}

и либо отсутствие оператора return, либо аргумента даст вам ошибку компилятора.

Поэтому, если вы объявляете функцию, которая принимает аргумент и возвращает один, я думаю, что невозможно преобразовать его в функцию, которая не имеет ни одного из упомянутых выше.

Ответ 8

Просто для справки, какой функциональный интерфейс можно использовать для ссылки на метод в случаях, когда метод генерирует и/или возвращает значение.

void notReturnsNotThrows() {};
void notReturnsThrows() throws Exception {}
String returnsNotThrows() { return ""; }
String returnsThrows() throws Exception { return ""; }

{
    Runnable r1 = this::notReturnsNotThrows; //ok
    Runnable r2 = this::notReturnsThrows; //error
    Runnable r3 = this::returnsNotThrows; //ok
    Runnable r4 = this::returnsThrows; //error

    Callable c1 = this::notReturnsNotThrows; //error
    Callable c2 = this::notReturnsThrows; //error
    Callable c3 = this::returnsNotThrows; //ok
    Callable c4 = this::returnsThrows; //ok

}


interface VoidCallableExtendsCallable extends Callable<Void> {
    @Override
    Void call() throws Exception;
}

interface VoidCallable {
    void call() throws Exception;
}

{
    VoidCallableExtendsCallable vcec1 = this::notReturnsNotThrows; //error
    VoidCallableExtendsCallable vcec2 = this::notReturnsThrows; //error
    VoidCallableExtendsCallable vcec3 = this::returnsNotThrows; //error
    VoidCallableExtendsCallable vcec4 = this::returnsThrows; //error

    VoidCallable vc1 = this::notReturnsNotThrows; //ok
    VoidCallable vc2 = this::notReturnsThrows; //ok
    VoidCallable vc3 = this::returnsNotThrows; //ok
    VoidCallable vc4 = this::returnsThrows; //ok
}