likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 8

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

什么是函数式接口

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

常见函数式接口

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

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

自定义函数式接口

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

定义 CustomFunctionalInterface 接口函数

@java.lang.FunctionalInterface
public interface CustomFunctionalInterface {
   void execute();
}

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

基于 CustomFunctionalInterface 使用

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 函数式接口

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

基于 Predicate 进行筛选

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 函数式接口

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

基于 Consumer 进行筛选

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 的抽象方法,接受一个参数并返回一个结果。这个接口用于表示一个对输入数据的转换操作。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

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

基于 Function 进行数据转换

转换为大写
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 实现来执行转换操作。

字符串长度映射
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 的抽象方法,用于获取一个结果。这个接口用于表示一个无参操作,只产生结果。

javaCopy code
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

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

基于 Supplier 进行延迟计算

随机数生成
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 方法来获取随机数。

延迟初始化
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 方法,观察只有在需要时才会初始化资源。