150. Java Lambda 表达式 - Supplier 接口

64 阅读4分钟

150. Java Lambda 表达式 - Supplier 接口

Java SE 8 引入了 Lambda 表达式,并对 JDK API 进行了重写,使得 Lambda 能够与现有代码良好集成。引入 Lambda 表达式后,JDK 8 更新的类比 JDK 5 中引入泛型后的更新更多。这使得 Lambda 表达式可以广泛应用于应用程序中,并通过简洁的语法实现功能。

JDK 8 引入的 java.util.function

JDK 8 中,除了 Lambda 表达式外,java.util.function 包也得到了引入,这个包包含了大量的功能接口,这些接口在 JDK API 中被广泛使用,尤其是在集合框架(Collections Framework)和流 APIStream API)中。该包位于 java.base 模块中,提供了超过 40 个接口,初看之下可能让人感到有些困惑,但它们围绕着四个主要接口进行组织,了解这些主要接口能够帮助我们理解其他功能接口。

生产者(提供者)接口:Supplier<T>

Supplier<T>java.util.function 包中的第一个接口,它的功能是提供一个对象。简单来说,Supplier<T> 接口不接受任何参数,但返回一个对象。Lambda 表达式可以用来实现 Supplier 接口。

Supplier<T> 接口的定义
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
示例:使用 Supplier<String> 创建一个返回字符串的 Lambda 表达式
Supplier<String> supplier = () -> "Hello Duke!";
System.out.println(supplier.get());  // 输出 "Hello Duke!"

这个 Lambda 表达式简单地返回了 "Hello Duke!" 字符串。可以在实际应用中使用不同的 Supplier 实现来动态生成或提供不同的对象。

示例:使用 Supplier 生成随机数
Random random = new Random(314L);
Supplier<Integer> newRandom = () -> random.nextInt(10);

for (int index = 0; index < 5; index++) {
    System.out.println(newRandom.get() + " ");
}

在上面的示例中,newRandom 是一个 Supplier<Integer>,每次调用它的 get() 方法时,会生成一个随机数。由于随机数生成器的种子(314L)固定,因此生成的随机数是可预测的。

输出示例:
1
5
3
0
2

注意,在这个例子中,Lambda 表达式从外部作用域捕获了 random 变量。由于 random 是不可变的,它符合 Lambda 表达式的要求,即只能捕获 final 或实际是 final 的变量。

使用 Supplier<T> 接口

可以通过调用 get() 方法来获取 Supplier 提供的对象。例如:

for (int index = 0; index < 5; index++) {
    System.out.println(newRandom.get() + " ");
}

每次调用 get() 方法都会执行 Lambda 表达式中的代码,生成并返回一个新的随机数。

使用专用的生产者接口

为了提高性能,避免不必要的装箱(autoboxing)和拆箱(unboxing),JDK 8 提供了 Supplier<T> 接口的专用优化版本,用于处理原始类型。这些专用版本的接口可以避免装箱和拆箱的开销,因此在需要优化性能时尤为有用。

JDK 8 提供了以下几种专用的 Supplier 接口:

  • IntSupplier:返回 int 类型的值。
  • LongSupplier:返回 long 类型的值。
  • DoubleSupplier:返回 double 类型的值。
  • BooleanSupplier:返回 boolean 类型的值。
示例:使用 IntSupplier 生成随机整数
Random random = new Random(314L);
IntSupplier newRandom = () -> random.nextInt();

for (int i = 0; i < 5; i++) {
    int nextRandom = newRandom.getAsInt();
    System.out.println("next random = " + nextRandom);
}

这里,我们使用 IntSupplier 来避免不必要的装箱。与 Supplier<Integer> 不同,IntSupplier 返回的是原始类型 int,因此没有发生装箱和拆箱,性能更高。

输出示例:
next random = 1
next random = 5
next random = 3
next random = 0
next random = 2
专用 Supplier 接口的优势

通过使用专用的供应商接口(如 IntSupplierLongSupplierDoubleSupplierBooleanSupplier),可以在不进行装箱/拆箱的情况下处理原始类型数据。这样能够避免不必要的性能开销,特别是在性能要求较高的场景下,减少 CPU 周期的消耗。

总结

  • Lambda 表达式与 java.util.functionLambda 表达式与 java.util.function 包中的接口紧密结合,为开发者提供了简单而强大的工具来处理函数式编程任务。
  • Supplier<T> 接口:用于生成对象,Supplier 不接受参数,只返回一个对象。可以使用它来动态生成和提供对象,如生成随机数等。
  • 专用 Supplier 接口:为了提高性能,避免装箱/拆箱操作,JDK 提供了专门的接口(如 IntSupplierLongSupplier 等)来处理原始类型。

通过理解和掌握这些概念,可以在 Java 中更高效地使用 Lambda 表达式,并根据需要选择合适的接口来优化性能。