Java 简明教程

Java - Functional Interfaces

Functional interfaces 在 Java 8 中引入,以及 lambda expressionmethod references 。这三个特性被添加来提升 Java 中函数式编程并编写干净、可读的代码。在 Java 8 之前,必须编写大量样板代码来涵盖基本功能。例如,为了调用函数,我们首先必须创建一个带有所需方法的类、创建一个类实例,而实例需要调用方法,或者另一种方法是使用具有相应方法的 anonymous class

使用 Lambda 表达式,我们可以避免对具体和匿名类对象的需求。functional interface 协助向前迈进了一步,因为 Lambda 表达式可以轻松地实现函数式接口,只要实现一个方法。

Functional interfaces 有一个要展示的单一功能。例如,带有单个方法 compareTo() 的 Comparable 接口用于比较目的。但它可以拥有任意数量的默认和静态方法。

Java 8 定义了很多函数式接口,以便在 Lambda 表达式中广泛使用。以下是 java.util.Function 包中定义的函数式接口列表。

@FunctionalInterface Annotation

按功能,任何接口只要有一个单一抽象方法就是一个函数式接口。Java 提供了 @FunctionalInterface 注解来标记一个接口为函数式接口,这样编译器可以检查一个接口是否属于函数式接口。这个注解是可选的,它主要目的是添加编译器检查并提升代码的可读性和可维护性。

Types of Functional Interfaces in Java

在 Java 中主要有四种类型的函数式接口。

Predicate Functional Interface

谓词式函数式接口是一个其方法接受一个自变量并返回 true 或 false 的接口。谓词式函数式接口主要用于比较元素或基于应用于传递给输入的特定条件(某些条件)对值进行过滤。Java 同时提供针对基本类型的谓词式函数式接口,如 IntPredicate、DoublePredicate 和 LongPredicate,它们分别只接受 Integer、Double 和 Long。

Predicate predicate = (value) -> value  != 0;
// or
Predicate predicate = (value) -> test(value);

在上面的片段中,谓词函数的作用是基于传递给它的值返回 true/false。

在这个示例中,我们正在使用一个谓词式函数式接口,借助一个 Lambda 表达式,从一个整数列表中过滤出奇数。

package com.tutorialspoint;

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

public class Tester {
   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

      Predicate<Integer> isEvenNumber = n -> n %2 == 0;
      numbers =  numbers.stream().filter(isEvenNumber).toList();

      System.out.println(numbers);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

[2, 4, 6, 8]

Consumer Functional Interface

消费式函数式接口是一个其方法接受一个自变量且不返回任何内容的接口。消费式函数式接口主要用于产生副作用的操作。例如,打印一个元素,添加一个敬语等。也存在如 BiConsumer 等 Consumer 的其他变体。BiConsumer 函数式接口可以接受两个参数。Java 同时提供针对基本类型的消费者式函数式接口,如 IntConsumer、DoubleConsumer 和 LongConsumer,它们分别只接受 Integer、Double 和 Long。

Consumer consumer = (value) -> System.out.println(value);
// Or
Consumer consumer1 = System.out::println;
// Or
Consumer consumer2 = (value) -> accept(value);

在这个示例中,我们正在使用一个消费者式函数式接口,借助一个 Lambda 表达式和一个方法引用,从一个整数列表中打印所有数字。

package com.tutorialspoint;

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

public class Tester {
   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

      Consumer<Integer> consumer = (value) -> System.out.println(value);
      Consumer consumer1 = System.out::println;

      System.out.println("Printing using consumer functional interface as lambda expression");
      numbers.forEach(consumer);

      System.out.println("Printing using consumer functional interface as method reference");
      numbers.forEach(consumer1);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

Printing using consumer functional interface as lambda expression
1
2
3
4
5
6
7
8
Printing using consumer functional interface as method reference
1
2
3
4
5
6
7
8

Supplier Functional Interface

供应商式函数式接口是一个其方法没有要传递的任何自变量且将返回一个值的接口。供应商式函数式接口主要用于懒惰地生成值。例如,获取一个随机数,生成一个数字序列等。

Supplier supplier = () -> Math.random() * 10;
// or
Supplier supplier1 = () -> get();

在这个示例中,我们正在使用一个供应商式函数式接口,借助一个 Lambda 表达式,获取一个随机数。

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

public class Tester {
   public static void main(String args[]) {
      Supplier<Integer> supplier = () -> (int)(Math.random() * 10);

      List<Integer> randomNumbers = new ArrayList<>();

      // generate 10 random numbers
      for(int i = 0; i< 10; i++) {
         randomNumbers.add(supplier.get());
      }
      System.out.println(randomNumbers);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

[0, 8, 8, 8, 8, 5, 7, 5, 5, 9]

Function Functional Interface

Function 函数式接口是不接受一个参数并且会返回一个值的方法。函数式接口通常用于获取处理过后的值。比如,获取一个元素的平方,修整字符串值等。Function 还有其它变体,例如 BiFunction。BiFunction 函数式接口可以接受两个参数。Java 既提供了基本类型的函数式接口,也提供了只接受 Integer、Double 和 Long 的 IntFunction、DoubleFunction 和 LongFunction 等。还有两个效用接口,分别为扩展 Function 接口的 UnaryOperator 和扩展 BiFunction 接口的 BinaryOperator。

Function function = (value) -> Math.random() * 10;
// or
Function function1 = (value) -> apply(value);

在这个示例中,我们使用函数式接口来借助一个 lambda 表达式获取一个数字的平方列表。

package com.tutorialspoint;

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

public class Tester {
   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

      Function<Integer, Integer> squared = (value) -> value * value;

      List<Integer> squaredNumbers =  numbers.stream().map(squared).toList();

      System.out.println(squaredNumbers);
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

[1, 4, 9, 16, 25, 36, 49, 64]

Existing Functional Interfaces Prior to Java 8

在 Java 中,很多现存的接口被标记为函数式接口,并且可以在 lambda 表达式中使用。比如:

  1. Runnable − 提供 run() 方法

  2. Callable − 提供 call() 方法

  3. Actionlistener − 提供 actionPerformed() 方法

  4. Comparable − 提供 compareTo() 方法用于比较两个数字

Example

在这个示例中,我们创建了两个线程。第一个使用匿名类创建,第二个使用 lambda 表达式创建。两者都使用可运行接口创建线程实例。

package com.tutorialspoint;

public class Tester {
   public static void main(String args[]) {
      // create anonymous inner class object
      new Thread(new Runnable() {
         @Override public void run() {
            System.out.println("Thread 1 is running");
         }
      }).start();

      // lambda expression to create the object
      new Thread(() -> {
         System.out.println("Thread 2 is running.");
      }).start();
   }
}

让我们编译并运行上述程序,这将生成以下结果 −

Thread 1 is running
Thread 2 is running.