Java 8 Lambda Void参数


187

假设我在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类型参数?



7
如果您需要定义的动作,则不可能。然而,你的第一个例子可以适合在一个Runnable,这是你正在寻找Runnable r = () -> System.out.println("Do nothing!");
亚历克西斯C.

1
@BobTheBuilder我不想使用该帖子中建议的使用者。
Wickoo

2
Matt的回答使这些类型起作用,但是当调用者获得空返回值时,它会做什么?
Stuart Marks

8
可以交叉手指,希望建议2&3在这个帖子得到接受的Java 9!
assylias 2015年

Answers:


110

你以后的语法可能与一个小帮手功能,其转换RunnableAction<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"));

4
IMO,这是您可以获得的最干净的解决方法,因此+1(或使用界面本身的静态方法)
Alexis C.

康斯坦丁·约夫科夫(Konstantin Yovkov)的以下解决方案(带有@FunctionalInterface)是一种更好的解决方案,因为它不涉及泛型且不需要额外的代码。
uthomas

@uthomas对不起,我没有涉及的答案@FunctionalInterface。他只是说,不可能将其扩展……
马特

1
嗨@Matt,对不起。我反应太快了。对于给定的问题,您的答案是完全有效的。不幸的是,我的投票已锁定,因此我无法在这个答案上删除-1。有两个注意事项:1.除了Runnable采取行动之外,还应该使用@FunctionalInterface名为的自定义内容SideEffect; 2.对此类辅助函数的需要凸显出某种奇怪的事情正在发生,并且抽象可能已被破坏。
uthomas

529

使用Supplier如果它需要什么,但回报的东西。

使用Consumer如果它需要的东西,但没有任何回报。

使用Callable如果它返回一个结果,并可能会抛出(最类似于Thunk一般的CS而言)。

使用Runnable如果它既不并不能扔。


例如,我这样做是为了包装“ void”返回调用:public static void wrapCall(Runnable r) { r.run(); }。感谢
Maxence

13
漂亮的答案。简短而精确。
克林特·伊斯特伍德

不幸的是,如果必须抛出一个已检查的异常并没有帮助。
杰西·格里克

13
作为此答案的完成,这不值得编辑:您还可以使用BiConsumer(获取2,返回0),Function(获取1,返回1)和BiFunction(获取2,返回1)。这些是最重要的要知道
CLOVIS '18

2
是否有类似Callable(在其call()方法中引发异常)之类的东西,但需要任何返回值吗?
dpelisek

40

λ:

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

实际上代表一个接口的实现,例如:

public interface Something {
    void action();
}

这与您定义的完全不同。这就是为什么您会得到一个错误。

由于您不能扩展自己的产品@FunctionalInterface,也不能引入全新的产品,所以我认为您没有太多选择。但是,您可以使用Optional<T>接口来表示缺少某些值(返回类型或方法参数)。但是,这不会使lambda主体更简单。


问题是您的Something函数不能是我的Action类型的子类型,并且我不能有两种不同的类型。
Wickoo,2015年

从技术上讲他可以,但是他说他想避免这种情况。:)
Konstantin Yovkov 2015年

31

您可以为这种特殊情况创建一个子接口:

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

它使用默认方法覆盖继承的参数化方法Void execute(Void),将调用委派给更简单的方法void execute()

结果是它使用起来更加简单:

Command c = () -> System.out.println("Do nothing!");

这个Action <Void,Void>从哪里来?Swing或JAX-WX Action接口都没有这样的通用接口吗?
luis.espinal

1
@ luis.espinal:Action<T, U>在这个问题被宣布.....
Jordão酒店

哈哈哈,我怎么想念那个?谢谢!
luis.espinal

5

这是不可能的。具有非无效返回类型(即使是Void)的函数也必须返回一个值。但是,您可以向其中添加静态方法Action,以“创建” a 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));

5

我认为这张桌子简短实用:

Supplier       ()    -> x
Consumer       x     -> ()
Callable       ()    -> x throws ex
Runnable       ()    -> ()
Function       x     -> y
BiFunction     x,y   -> z
Predicate      x     -> boolean
UnaryOperator  x1    -> x2
BinaryOperator x1,x2 -> x3

如其他答案所述,该问题的适当选择是 Runnable


4

在功能界面中添加静态方法

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!

3

我认为这是不可能的,因为函数定义在您的示例中不匹配。

您的lambda表达式的计算结果与

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语句或参数都会给您带来编译器错误。

因此,如果声明一个带有参数并返回一个参数的函数,我认为不可能将其转换为既不执行上述任何一个操作的函数。


3

仅作为参考,在方法抛出和/或返回值的情况下,可以使用哪个功能接口作为方法参考。

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
}

请添加更多上下文。这看起来很有趣,但是其含义并不立即显而易见。
bnieland '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.