Java函数式接口

函数式接口概览

函数式接口是一种特殊类型的接口,它只有一个抽象方法,因此可以实现函数作为一等公民(first-class functions),即函数可以像任何其他对象一样被传递和操作。

基本概念

  1. 只有一个抽象方法的接口:这是函数式接口的基本定义。除了这个抽象方法,函数式接口可以有多个默认方法或静态方法
  2. @FunctionalInterface 注解:这是一个元注解,用于明确标识一个接口是函数式接口。虽然这个注解不是必需的,但它有两个用途:一是提供编译时检查,确保接口只有一个抽象方法;二是作为一种文档说明,表明该接口是设计为函数式接口的。
  3. Lambda 表达式:Lambda 表达式是一种简洁的方式来实现函数式接口的匿名实现。Lambda 允许你以更简洁的形式表达单方法接口的实现。
  4. 方法引用:方法引用是一种使用现有方法作为Lambda表达式的语法糖。它允许你引用类的方法或者构造函数,而不是显式地实现整个函数式接口。
  5. 构造函数引用:与方法引用类似,构造函数引用允许你引用一个类的构造函数。
  6. 泛型函数式接口:Java 8 提供了几个泛型函数式接口,如 Function<T,R>Predicate<T>Consumer<T>Supplier<T> 等,它们分别用于代表不同的函数操作,比如函数映射、条件判断、消费操作和供应操作。

Java 8 中的函数式接口

以下是 Java 8 提供的一些核心函数式接口:

  • Supplier<T>:返回类型为 T 的结果,无参数。
  • Consumer<T>:接受一个参数,执行某种操作,无返回值。
  • Function<T,R>:接受一个类型为 T 的参数,并返回类型为 R 的结果。
  • Predicate<T>:接受一个类型为 T 的参数,并返回一个布尔值。
  • BinaryOperator<T>:接受两个类型为 T 的参数,并返回类型为 T 的结果。
  • UnaryOperator<T>:接受一个类型为 T 的参数,并返回类型为 T 的结果
import java.util.function.BiConsumer;  
import java.util.function.BiFunction;  
import java.util.function.BinaryOperator;  
import java.util.function.Consumer;  
import java.util.function.Function;  
import java.util.function.Predicate;  
import java.util.function.Supplier;  
import java.util.function.ToDoubleFunction;  
import java.util.function.ToIntFunction;  
import java.util.function.ToLongFunction;

Consumer

基本信息

Consumer 接口是 java.util.function 包的一部分。Consumer 接口的泛型声明如下:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

Consumer 接口的目的是执行一个操作,它接受一个输入参数,并返回 void。也就是说,它接受了一个参数,并产生了一些结果,但是它是没有返回值的。accept 方法是 Consumer 接口的唯一抽象方法,它接受一个参数并执行某些操作。

Consumer 的使用

Consumer 接口通常用在需要对集合中的每个元素执行操作的场景中,比如在 forEach 方法中。

示例1:使用 forEach 方法

image

package org.example;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }
}

在这个例子中,我们使用 forEach 方法遍历列表,并为每个元素执行 accept 方法。这里我们创建了一个匿名内部类来实现 Consumer 接口。

示例2:使用 Lambda表达式改写

在函数式编程中,可以使用 lambda 表达式简化代码,因此上述代码还能够写为:

package org.example;

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        list.forEach(s -> System.out.println(s));
    }
}

这里我们直接提供了一个 lambda 表达式作为参数,它实现了 accept 方法。

示例3:使用 forEach 方法和方法引用

如果我们的方法是接收一个参数,并执行操作,我们就可以使用方法引用:

package org.example;

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        list.forEach(System.out::println);
    }
}

或者我直接自定义一个方法来更好地体现「接受一个参数,并执行操作」的含义:

package org.example;

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void print(String s) {
        System.out.println(s);
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        list.forEach(Main::print);
    }
}

在这个例子中,我们使用方法引用 Main::print 来引用 print 方法,它接受一个 String 参数并打印它。

andThen 方法

andThenConsumer 接口提供的一个默认方法,它允许你将两个 Consumer 组合在一起,以便在同一个元素上顺序执行两个操作。这个方法的声明如下:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

andThen 方法的工作原理

当你调用 andThen 方法时,你需要提供一个 Consumer 对象作为参数。andThen 方法会返回一个新的 Consumer 对象,这个新对象在它的 accept 方法中会先调用当前 Consumeraccept 方法,然后调用传入的 Consumer 对象的 accept 方法。

andThen 方法的使用示例

假设我们有两个 Consumer 对象,一个用于打印字符串,另一个用于将字符串转换为大写:

Consumer<String> printConsumer = System.out::println;
Consumer<String> toUpperCaseConsumer = s -> System.out.println(s.toUpperCase());

我们可以使用 andThen 方法将这两个操作组合在一起,使得每个字符串首先被打印,然后它的大写形式也被打印:

Consumer<String> combinedConsumer = printConsumer.andThen(toUpperCaseConsumer);

List<String> list = Arrays.asList("apple", "banana", "cherry");
list.forEach(combinedConsumer);

输出结果将是:

apple
APPLE
banana
BANANA
cherry
CHERRY

在这个例子中,andThen 方法创建了一个新的 Consumer,它将两个操作串联起来。对于列表中的每个元素,都会先执行 printConsumer 的操作(打印字符串),然后执行 toUpperCaseConsumer 的操作(打印字符串的大写形式)。

注意事项

  • andThen 方法返回一个新的 Consumer 对象,因此它不会修改原始的 Consumer 对象。
  • 如果任何一个 Consumer 抛出异常,后续的 Consumer 将不会被执行。
  • andThen 方法可以链式调用,即你可以将多个 Consumer 组合在一起,例如 consumer1.andThen(consumer2).andThen(consumer3)

Function

Predicate

Supplier

References

  1. A Deep Dive into Supplier, Consumer, and Function in Java 8. | by Java Fusion | Medium
  2. Java 8 | Consumer Interface in Java with Examples - GeeksforGeeks
  3. Java 8 Predicate with Examples - GeeksforGeeks
  4. Function Interface in Java with Examples - GeeksforGeeks
  5. Supplier Interface in Java with Examples - GeeksforGeeks
  6. Method References in Java with examples - GeeksforGeeks
posted @ 2024-11-02 01:21  Lim_YK  阅读(678)  评论(0)    收藏  举报