带你理解 Java 8 的函数式接口使用和自定义

函数式接口是 Java 8 引入的一种接口,用于支持函数式编程。函数式接口通常包含一个抽象方法,可以被 Lambda 表达式或方法引用所实现。在本文中,我们将深入探讨函数式接口的概念、用途以及如何创建和使用函数式接口。

什么是函数式接口

函数式接口是只包含一个抽象方法的接口。但是默认方法和静态方法在此接口中可以定义多个。Java 中的函数式接口可以被用作 Lambda 表达式的目标类型。通过函数式接口,可以实现更简洁、更具可读性的代码,从而支持函数式编程的思想。

常见函数式接口

Java 中有一些内置的函数式接口,用于不同的用途:

  1. Runnable 用于描述可以在单独线程中执行的任务。
  2. Callable 类似于 Runnable,但可以返回执行结果或抛出异常。
  3. Comparator 用于比较两个对象的顺序。
  4. Function 接受一个参数并产生一个结果。
  5. Predicate 接受一个参数并返回一个布尔值,用于判断条件是否满足。
  6. Supplier 不接受参数,但返回一个值。

自定义函数式接口

自定义函数式接口是需要在接口上添加 @FunctionalInterface 注解

定义 CustomFunctionalInterface 接口函数

java 复制代码
@java.lang.FunctionalInterface
public interface CustomFunctionalInterface {
   void execute();
}

在上述代码中,定义了一个名为 CustomFunctionalInterface 的函数式接口,其中包含一个抽象方法 execute,这是一个无参无返回值的方法。通过使用 @FunctionalInterface 注解,明确告诉编译器这是一个函数式接口,确保它只包含一个抽象方法。

基于 CustomFunctionalInterface 使用

java 复制代码
public class CustomFunctionInterfaceTest {

  public void test(CustomFunctionalInterface functionalInterface) {
    functionalInterface.execute();
  }

  @Test
  public void execute() {
    test(() -> System.out.println("Hello World Custom!"));
  }
}

在测试类 CustomFunctionInterfaceTest 中,定义了一个名为 test 的方法,接受一个 CustomFunctionalInterface 参数,并在方法体中调用了 execute 方法。这个方法允许将任意实现了 CustomFunctionalInterface 的 Lambda 表达式传递进来,并执行其 execute 方法。

在测试方法 execute 中,通过调用 test 方法,传递了一个 Lambda 表达式 () -> System.out.println("Hello World Custom!")。这个 Lambda 表达式实现了 CustomFunctionalInterface 的抽象方法 execute,即打印了一条 "Hello World Custom!" 的消息。

常见函数式接口基本使用

Predicate

当涉及到对集合或数据进行筛选时,Java 中的函数式接口 Predicate 可以发挥重要作用。Predicate 是一个通用的函数式接口,用于定义一个接受参数并返回布尔值的操作,用于判断条件是否满足。

Predicate 函数式接口

java 复制代码
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

基于 Predicate 进行筛选

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class PredicateExample {

    public static List<String> filterStrings(List<String> list, Predicate<String> predicate) {
        List<String> filteredList = new ArrayList<>();
        for (String str : list) {
            if (predicate.test(str)) {
                filteredList.add(str);
            }
        }
        return filteredList;
    }

    public static void main(String[] args) {
        List<String> stringList = List.of("apple", "banana", "cherry", "date", "elderberry");
        Predicate<String> lengthPredicate = str -> str.length() > 5;
        List<String> longStrings = filterStrings(stringList, lengthPredicate);
        System.out.println("Long strings: " + longStrings);
    }
}

在这个示例中,我们定义了一个 filterStrings 方法,它接受一个字符串列表和一个 Predicate 参数,并返回符合条件的字符串列表。在 main 方法中,我们创建了一个长度判断的 Predicate,然后使用它来筛选出长度大于 5 的字符串。

Consumer

函数式接口 Consumer 在 Java 中用于表示接受一个参数并且没有返回值的操作。它可以用于执行一些对输入数据的处理,例如打印、修改等。

Consumer 函数式接口

java 复制代码
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

基于 Consumer 进行筛选

java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerExample {

        public static void processIntegers(List<Integer> list, Consumer<Integer> consumer) {
        for (Integer num : list) {
            consumer.accept(num);
        }
    }

    public static void main(String[] args) {
        List<Integer> integerList = List.of(1, 2, 3, 4, 5);
        Consumer<Integer> squareAndPrint = num -> {
            int square = num * num;
            System.out.println("Square of " + num + " is: " + square);
        };
        processIntegers(integerList, squareAndPrint);
    }
}

在这个示例中,我们定义了一个 filterStrings 方法,它接受一个字符串列表和一个 Predicate 参数,并返回符合条件的字符串列表。在 main 方法中,我们创建了一个长度判断的 Predicate,然后使用它来筛选出长度大于 5 的字符串。

在这个示例中,我们定义了一个 processIntegers 方法,它接受一个整数列表和一个 Consumer 参数,并在方法内遍历列表,对每个元素执行 accept 方法。在 main 方法中,我们创建了一个 Consumer 实现 squareAndPrint,它会计算每个元素的平方并打印出来。

Function

函数式接口 Function 在 Java 中用于表示一个接受一个参数并产生一个结果的操作。它可以用于执行各种转换、映射和处理操作。

Function 函数式接口

Function 接口定义了一个名为 apply 的抽象方法,接受一个参数并返回一个结果。这个接口用于表示一个对输入数据的转换操作。

java 复制代码
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

在上述定义中,T 表示输入类型,R 表示输出类型。

基于 Function 进行数据转换

转换为大写
java 复制代码
import java.util.function.Function;

public class FunctionExample {

    public static String convertToUpperCase(String input, Function<String, String> function) {
        return function.apply(input);
    }
    public static void main(String[] args) {
        String original = "hello world";
        String upperCase = convertToUpperCase(original, str -> str.toUpperCase());
        System.out.println(upperCase);
    }
}

在这个示例中,我们定义了一个 convertToUpperCase 方法,它接受一个字符串和一个 Function 参数,用于将输入字符串转换为大写。在 main 方法中,我们通过传递一个 Function 实现来执行转换操作。

字符串长度映射
java 复制代码
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FunctionExample {

    public static List<Integer> mapStringLengths(List<String> list, Function<String, Integer> function) {
        return list.stream()
                .map(function)
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        List<String> strings = List.of("apple", "banana", "cherry", "date");
        List<Integer> lengths = mapStringLengths(strings, str -> str.length());
        System.out.println(lengths);
    }
}

在这个示例中,我们定义了一个 mapStringLengths 方法,它接受一个字符串列表和一个 Function 参数,用于将输入字符串映射为它们的长度。通过使用 map 操作,我们在列表中的每个字符串上执行了长度映射。

Supplier

函数式接口 Supplier 在 Java 中用于表示一个不接受参数但产生一个结果的操作。它通常用于延迟计算,只在需要时才执行操作并生成结果。

Supplier 函数式接口

Supplier 接口定义了一个名为 get 的抽象方法,用于获取一个结果。这个接口用于表示一个无参操作,只产生结果。

csharp 复制代码
javaCopy code
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

在上述定义中,T 表示结果的类型

基于 Supplier 进行延迟计算

随机数生成
java 复制代码
import java.util.Random;
import java.util.function.Supplier;

public class SupplierExample {

    public static int generateRandomNumber(Supplier<Integer> supplier) {
        return supplier.get();
    }

    public static void main(String[] args) {
        Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
        int randomNumber = generateRandomNumber(randomSupplier);
        System.out.println("Random number: " + randomNumber);
    }
}

在这个示例中,我们定义了一个 generateRandomNumber 方法,它接受一个 Supplier 参数,并通过调用 get 方法获取随机数。在 main 方法中,我们创建了一个随机数生成的 Supplier,然后将它传递给 generateRandomNumber 方法来获取随机数。

延迟初始化
java 复制代码
import java.util.function.Supplier;

public class SupplierExample {

    private String expensiveResource = null;

    public String getExpensiveResource(Supplier<String> supplier) {
        if (expensiveResource == null) {
            expensiveResource = supplier.get();
        }
        return expensiveResource;
    }

    public static void main(String[] args) {
        Supplier<String> resourceSupplier = () -> {
            System.out.println("Initializing expensive resource...");
            return "Expensive Resource";
        };
        SupplierExample example = new SupplierExample();
        System.out.println(example.getExpensiveResource(resourceSupplier));
        System.out.println(example.getExpensiveResource(resourceSupplier));
    }
}

在这个示例中,我们定义了一个 getExpensiveResource 方法,它接受一个 Supplier 参数,并使用延迟初始化的方式获取昂贵的资源。在 main 方法中,我们创建了一个资源初始化的 Supplier,然后多次调用 getExpensiveResource 方法,观察只有在需要时才会初始化资源。

相关推荐
lUie INGA2 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
geBR OTTE2 小时前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
Porunarufu2 小时前
博客系统UI自动化测试报告
java
NineData2 小时前
NineData 新增支持 GaussDB 到 StarRocks 实时数据复制能力
后端
sghuter3 小时前
数字资源分发架构解密
后端·架构·dubbo
小码哥_常3 小时前
Spring Boot启动慢?这5个优化点带你起飞
后端
NineData3 小时前
NineData将亮相DACon 2026上海站!解锁AGI时代数据“智理”新范式
数据库·后端·架构
Aurorar0rua3 小时前
CS50 x 2024 Notes C - 05
java·c语言·数据结构
Cosmoshhhyyy4 小时前
《Effective Java》解读第49条:检查参数的有效性
java·开发语言