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
}