Java中使用函数式接口的现实世界示例

6

我知道函数式接口指的是只能有一个抽象方法,但可以有多个默认方法,但我想知道如何用Java中的函数式接口举一个现实世界的例子/情境。

你能给我一个有效的情况/示例吗?

谢谢!


1
Lambda是函数接口的实现,因此它们将被隐式地(由编译器或运行时)或显式地(通过代码分配)使用。一个实际的例子是在代码中使用Predicate进行过滤。 - Amit
5个回答

6

首先,Java内置的函数式接口 PredicateFunctionConsumer 等都使用了注解 @FunctionalInterface

另一方面,您可能想创建自定义的函数式接口,如下所示:

@FunctionalInterface
public interface ThrowingConsumer<T> {
    void accept(T t) throws CustomException;
}

然后您可以将其用作方法参数:

public <T, R> void doSomething(T value, ThrowingConsumer<T, R> consumer) {
    // ...
}

然后像这样调用它:

doSomething(someValue, this::customConsumerMethodThrowingAnException);

值得一提的是,@FunctionalInterface 不是必须的。编译器可以接受任何符合要求的接口。

编译器处理它的方式类似于处理 @Override 注解。即使没有它,代码也可以编译。但一旦添加了它,将使代码更清晰、更安全,方便未来维护代码的人员。


你能否添加一条注释,说明 @FunctionalInterface 并非必需,但只是为了防止将 SMIs 用作 lambda 时添加方法而破坏接口兼容性。否则,此答案仅涉及 SMIs - 而不涉及 @FunctionalInterface... - Boris the Spider

5

JDK8之前我们已经有了函数式接口,但没有 lambda 表达式、方法引用等功能。

从 JDK8 开始,它们为 lambda 表达式和方法引用提供了目标类型,从而使代码更易读,更紧凑。

例如,在 Java-8 之前,如果您想要提供一些逻辑,每次单击 Button 组件时都会执行:

 btn.setOnAction(new EventHandler<ActionEvent>() { 
       @Override
       public void handle(ActionEvent event) {
            System.out.println("Hello World!");
       }
 });

这篇文章看起来臃肿、难以阅读且不够紧凑。因为EventHandler 是一个函数式接口,也就是说它有一个单一抽象方法(SAM)。从 JDK8 开始,你可以这样写:

btn.setOnAction(event -> System.out.println("Hello World!"));

你只看到你关心的代码部分,即当单击按钮时要执行的逻辑。
此外,由于我们可以使用函数接口作为lambda表达式和方法引用的目标类型,因此这在以下情况下非常有用:
- 将比较器传递给排序方法,例如List.sort,Stream.sorted,Collections.sort等。 - 将一段代码块传递给在单独的线程中运行任务
等等...
同时保持代码可读性,紧凑性和简洁性。
函数接口在Java流API中广泛使用。
除非没有一个符合您从java.util.function中的要求或者函数接口的名称不够易读,否则您没有理由创建自己的函数接口。
建议在创建函数接口时使用@FunctionalInterface注释,但不是必需的(标准库经常使用此功能)。
这使编译器检查已注释实体是否为具有单个抽象方法的接口,否则会报错。
这也在重构代码时能够帮助捕获错误。

3

他们提供的主要用途之一是可以使用lambda表达式方法引用创建功能接口的实例,同时还可以使用构造函数。例如,定义为Sample的功能接口:

@FunctionalInterface
public interface Sample {
    void ab();
}

可以通过一行代码进行实例化,如下所示:
Sample sample = () -> System.out.println("ab called");

然后在需要的地方调用:

sample.ab();

我将引用来自java.util.function包的Javadoc

Functional interfaces can provide a target type in multiple contexts, such as assignment context, method invocation, or cast context:

 // Assignment context
 Predicate<String> p = String::isEmpty;

 // Method invocation context
 stream.filter(e -> e.getSize() > 10)...

 // Cast context
 stream.map((ToIntFunction) e -> e.getSize())...
此外,这样的接口可以用@FunctionalInterface注解进行说明。

该注解不是编译器将接口识别为函数式接口的要求,而仅仅是一种捕捉设计意图并在识别设计意图发生意外违规时请求编译器帮助的辅助手段。

此外,对于现有的这些接口使用这些概念也是值得注意的,
编译器会将任何符合函数式接口定义的接口视为函数式接口,无论接口声明中是否存在FunctionalInterface注解。

1

被标记为FunctionalInterface的接口在期望具有适当参数和返回类型的lambda表达式的上下文中保证可用。除此之外,它们没有任何用处。可能会有一些优化,但在所有情况下都不重要。


1
Lambdas是Functional interface的实现,可以通过编译器或运行时隐式地(通过代码分配)或显式地使用。一个实际的例子是:
  1. Predicate:用于过滤的跨代码使用。
  2. Functions:Map.computeIfAbsent("xxx", s -> s.length())。
  3. BiFunction:salaries.replaceAll((name, oldValue) -> name.equals("Freddy") ? oldValue : oldValue + 10000)。
  4. Consumers:List.forEach(name -> System.out.println("Hello, " + name))。
请注意,保留了HTML标记。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接