为什么必须要明确表示一个lambda是Consumer才能使用andThen方法?

时间:2018-08-14 08:01:20

标签: java lambda java-8 functional-programming

当我尝试使用带有两个void方法的函数组合时,我遇到了一个奇怪的(对我而言)行为。我写了一个简单的例子来说明问题:

debug_response

当我尝试编译第一个解决方案时,在andThe调用之前说“')'期望”。

当我明确地说这是“消费者”时,代码已编译并且可以按预期工作。

有人可以向我解释为什么会这样吗,还有使用Java 8进行void方法的函数组合的另一种方式吗?

3 个答案:

答案 0 :(得分:4)

这与Java推断,转换和检测lambda中的类型的方式有关。如上面的评论中所述,尚未进行到Consumer<Foo>的转换,这意味着编译器不知道这是Consumer,因此以后可以链接andThen()。< / p>

将其显式转换为Consumer并正确使用括号将使您获得所需的效果:

List<Foo> foos = new ArrayList<>();
foos.forEach(((Consumer<Foo>) Null::doSomething).andThen(Null::doSomething2));

我猜想如果您随便摆弄它,可以使用类型见证人实现相同的行为,但是我不确定100%是否可以达到预期的结果。

我第一次注意到这是使用链接比较器,它们可能表现出相同的行为。对此进行在线搜索将向您显示有关其工作原理的更多复杂细节。

答案 1 :(得分:4)

让我们变得更简单:

 private static boolean test(String input){
       return input.equals("123");
 }

 Predicate<String> predicate = YourClass::test;
 Function<String, Boolean> function = YourClass::test;

因此,方法引用是 poly 表达式(例如,类似于泛型),它们取决于使用它们的上下文。因此,您的Startup::doSomething方法引用可以是符合该方法的 any @FunctionalInterface。在这种情况下,您可能会发现它是Consumer,但对于编译器来说却是另一回事。

答案 2 :(得分:1)

就像Consumer所述:

  

这是一个功能接口,因此可以用作lambda表达式或方法引用的分配目标

功能接口为我们提供了两种方法:

  1. void accept(T t)
  2. default Consumer<T> andThen(Consumer<? super T> after)

关于andThen(...)

  

返回一个组成的使用者,该使用者依次执行此操作,然后执行after操作。

功能接口是Java 8提供的语法糖,我们可以只传递lambdamethod reference,我们可以获得更多有用/辅助的功能,我们经常需要(默认行为)。

在这里,我们可以使用andThen

轻松地将多个功能组合在一起

对于您的情况,您可以尝试以下操作:

public class CastToFunctionalInterface {
    public static void main(String... args) {
        ((Consumer<Integer>) CastToFunctionalInterface::consumeInteger)
                .andThen(CastToFunctionalInterface::consumeAnotherInteger)
                .accept(10);
    }

    private static void consumeInteger(Integer a) {
        System.out.println("I'm an Integer: " + a);
    }

    private static void consumeAnotherInteger(Integer b) {
        System.out.println("I'm another integer: " + b);
    }
}

输出:

I'm an Integer: 10
I'm another integer: 10