JAVA 常用函数式接口入门

  • 时间:2025-11-22 23:08 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:Java 开发工具包(JDK)中有许多函数式接口。这里我们来回顾一下常用的入门接口集合,这些接口是我们常常会遇到且需要熟悉的。我们在这里看到的所有接口都属于 java.util.function 包。Consumer<T>描述:表明一个接受输入但不返回任何结果的操作。要使其发挥作用,它必须产生副作用。抽象方法:accept默认方法:andThen常见用法:作为 forEach 方法的参

Java 开发工具包(JDK)中有许多函数式接口。这里我们来回顾一下常用的入门接口集合,这些接口是我们常常会遇到且需要熟悉的。我们在这里看到的所有接口都属于 java.util.function 包。

Consumer<T>

  • 描述:表明一个接受输入但不返回任何结果的操作。要使其发挥作用,它必须产生副作用。
  • 抽象方法:accept
  • 默认方法:andThen
  • 常见用法:作为 forEach 方法的参数
  • 基本类型特化:IntConsumer、LongConsumer、DoubleConsumer 等

Supplier<T>

  • 描述:一个工厂,预期返回一个新实例或预先创建的实例。
  • 抽象方法:get
  • 默认方法:无
  • 常见用法:用于创建惰性无限流,以及作为 Optional 类的 orElseGet 方法的参数
  • 基本类型特化:IntSupplier、LongSupplier、DoubleSupplier 等

Predicate<T>

  • 描述:用于检查输入参数是否满足某些条件。
  • 抽象方法:test
  • 默认方法:and、negate、or
  • 常见用法:作为 Stream 方法(如 filter 和 anyMatch)的参数
  • 基本类型特化:IntPredicate、LongPredicate、DoublePredicate 等

Function<T, R>

  • 描述:一个转换接口,表明一个接受参数并返回适当结果的操作。
  • 抽象方法:apply
  • 默认方法:andThen、compose
  • 常见用法:作为 Stream 的 map 方法的参数
  • 基本类型特化:IntFunction、LongFunction、DoubleFunction、IntToDoubleFunction、DoubleToIntFunction 等

语法概览

在本书中,我们一直在使用函数式接口、Lambda 表达式、方法引用和构造函数引用的新语法。本附录是一个快速的语法参考,使用从本书不同部分选取的示例代码。

定义函数式接口

@FunctionalInterface
public interface TailCall<T> {
    TailCall<T> apply();
    default boolean isComplete() { return false; }
    //...
}

函数式接口必须有一个抽象(未实现)方法。它可以有零个或多个默认方法或已实现的方法,也可以有静态方法。

创建无参数 Lambda 表达式

lazyEvaluator(() -> evaluate(1), () -> evaluate(2));

如果 Lambda 表达式没有参数,空参数列表周围的括号 () 是必需的。→ 用于分隔 Lambda 表达式的参数和主体。

创建单参数 Lambda 表达式

friends.forEach((final String name) -> System.out.println(name));

Java 编译器可以根据上下文推断 Lambda 表达式的类型。在某些上下文不足以进行推断或者我们希望代码更清晰的情况下,我们可以在参数名前指定类型。

推断 Lambda 表达式的参数类型

friends.forEach((name) -> System.out.println(name));

如果我们不提供参数类型,Java 编译器会尝试推断参数类型。使用推断类型可以减少代码冗余,并且更省力。但如果我们为一个参数指定了类型,就必须为 Lambda 表达式中的所有参数指定类型。

单参数推断类型省略括号

friends.forEach(name -> System.out.println(name));

如果 Lambda 表达式只接受一个参数并且其类型是推断得出的,参数周围的括号 () 是可选的。我们可以写成 name → … 或 (name) → …,提议使用前者,由于它更简洁。

创建多参数 Lambda 表达式

friends.stream()
   .reduce((name1, name2) ->
        name1.length() >= name2.length() ? name1 : name2);

如果 Lambda 表达式接受多个参数或没有参数,参数列表周围的括号 () 是必需的。

调用带有混合参数的方法

friends.stream()
   .reduce("Steve", (name1, name2) ->
        name1.length() >= name2.length() ? name1 : name2);

方法的参数可以是普通类、基本类型和函数式接口的混合。方法的任何参数都可以是函数式接口,我们可以将 Lambda 表达式或方法引用作为参数传递。

存储 Lambda 表达式

final Predicate<String> startsWithN = name -> name.startsWith("N");

为了便于复用和避免代码重复,我们一般希望将 Lambda 表达式存储在变量中。

创建多行 Lambda 表达式

FileWriterEAM.use("eam2.txt", writerEAM -> {
    writerEAM.writeStuff("how");
    writerEAM.writeStuff("sweet");
});

我们应该尽量保持 Lambda 表达式简短,但偶尔也会有多行代码的情况。此时需要使用花括号 {},并且如果 Lambda 表达式需要返回值,则必须使用 return 关键字。

返回 Lambda 表达式

public static Predicate<String> checkIfStartsWith(final String letter) {
    return name -> name.startsWith(letter);
}

如果方法的返回类型是函数式接口,我们可以在实则现中返回一个 Lambda 表达式。

从 Lambda 表达式返回 Lambda 表达式

final Function<String, Predicate<String>> startsWithLetter =
    letter -> name -> name.startsWith(letter);

我们可以构建本身返回 Lambda 表达式的 Lambda 表达式。这里 Function 接口的实现接受一个 String 类型的 letter,并返回一个符合 Predicate 接口的 Lambda 表达式。

闭包中的词法作用域

public static Predicate<String> checkIfStartsWith(final String letter) {
    return name -> name.startsWith(letter);
}

在 Lambda 表达式内部,我们可以访问外部方法作用域中的变量。例如,在 checkIfStartsWith 方法中,letter 变量在 Lambda 表达式内部被访问。绑定到外部作用域变量的 Lambda 表达式称为闭包。

传递实例方法的方法引用

friends.stream()
   .map(String::toUpperCase);

如果 Lambda 表达式直接将参数作为目标传递给一个简单的方法调用,我们可以用方法引用替换它。上述示例代码等同于:

friends.stream()
   .map(name -> name.toUpperCase());

传递静态方法的方法引用

str.chars()
   .filter(Character::isDigit);

如果 Lambda 表达式直接将参数作为参数传递给一个静态方法,我们可以用方法引用替换它。上述示例代码等同于:

str.chars()
   .filter(ch -> Character.isDigit(ch));

传递另一个实例上的方法的方法引用

str.chars()
   .forEach(System.out::println);

如果 Lambda 表达式直接将参数作为参数传递给另一个实例上的方法(例如 System.out 上的 println 方法),我们可以用方法引用替换它。上述示例代码等同于:

str.chars()
   .forEach(ch -> System.out.println(ch));

传递带参数方法的引用

people.stream()
   .sorted(Person::ageDifference)

如果 Lambda 表达式直接将第一个参数作为方法调用的目标,其余参数作为该方法的参数,我们可以用方法引用替换它。上述示例代码等同于:

people.stream()
   .sorted((person1, person2) -> person1.ageDifference(person2))

使用构造函数引用

Supplier<Heavy> supplier = Heavy::new;

我们可以使用简洁的构造函数引用语法让 Java 编译器创建对适当构造函数的调用,而不是直接调用构造函数。这与方法引用类似,只是它引用的是构造函数并会实例化对象。上述示例代码等同于:

Supplier<Heavy> supplier = () -> new Heavy();

函数组合

symbols.map(StockUtil::getPrice)
   .filter(StockUtil.isPriceLessThan(500))
   .reduce(StockUtil::pickHigh)
   .get();

我们可以组合函数,通过一系列操作来转换对象,就像这个示例一样。在函数式编程风格中,函数组合或链式调用是实现关联操作的强劲结构。

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】八股已死、场景当立(场景篇-设计模式篇)(2025-11-22 23:27)
【系统环境|】群、环、域(2025-11-22 23:26)
【系统环境|】深度解析:基于Python的分布式缓存系统实现与性能优化(2025-11-22 23:26)
【系统环境|】TP区块链下载全解析:从技术原理到代码实现(2025-11-22 23:25)
【系统环境|】大模型在急性肾衰竭预测及临床方案制定中的应用研究(2025-11-22 23:25)
【系统环境|】特价股票投资中的可持续供应链管理整合方法(2025-11-22 23:24)
【系统环境|】第193期 如何微调大语言模型(LLM)(内含源码细节)(2025-11-22 23:23)
【系统环境|】用Python构建智能推荐系统:技术赋能美好生活(2025-11-22 23:23)
【系统环境|】企业估值中的氢能源应用评估(2025-11-22 23:22)
【系统环境|】ansible 学习之路(2025-11-22 23:22)
手机二维码手机访问领取大礼包
返回顶部