为什么有必要明确地说 lambda 是消费者使用和然后方法?



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

public class Startup {
public static void main(String[] args) {
List<Foo> foos = new ArrayList<>();
// 1) Does not compile
foos.forEach(Startup::doSomething1.andThen(Startup::doSomething2));
Consumer<Foo> doSomething1 = Startup::doSomething1;
Consumer<Foo> doSomething2 = Startup::doSomething2;
// 2) Works as expected 
foos.forEach(doSomething1.andThen(doSomething2));
}
public static void doSomething1(Foo foo) {
}
public static void doSomething2(Foo foo) {
}
public static class Foo {
}
}

当我尝试编译第一个解决方案时,它会在然后调用之前说"'('预期"。

当我明确说这是消费者时,代码被编译并按预期工作。

谁能向我解释为什么会发生这种情况,是否有另一种方法 用 Java 8 对 void 方法进行函数组合?

让我们让这个更简单:

private static boolean test(String input){
return input.equals("123");
}
Predicate<String> predicate = YourClass::test;
Function<String, Boolean> function = YourClass::test;

因此,方法引用是一个多面表达式(例如泛型(,它们取决于使用它们的上下文。因此,您的Startup::doSomething方法引用可以是符合该方法的任何@FunctionalInterface。在这种情况下,您可能会认为这是一个Consumer,但对于编译器来说,这是一个不同的故事。

这与 Java 推断、转换和检测 lambda 中的类型的方式有关。如上面的注释中所述,尚未转换为Consumer<Foo>,这意味着编译器不知道这是一个Consumer,以便您可以在之后链接andThen()

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

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

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

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

正如消费者提到的:

这是一个函数接口,因此可以用作 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

相关内容

最新更新