返回顶部
首页 > 资讯 > 精选 >Java函数式编程怎么应用
  • 566
分享到

Java函数式编程怎么应用

2023-07-04 13:07:30 566人浏览 独家记忆
摘要

今天小编给大家分享一下Java函数式编程怎么应用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Java 根据常用需求场景的用

今天小编给大家分享一下Java函数式编程怎么应用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

Java 根据常用需求场景的用例,抽象出了几个内置的函数式接口给开发者使用,比如FunctionSupplier 等等,Stream 中各种操作方法的参数或者是返回值类型往往就是这些内置的函数式接口。

比如 Stream 中 map 操作方法的参数类型就是 Function

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

那为什么我们在平时使用 Stream 操作的 map 方法时,从来没有见过声明这个类型的参数呢?比如下面这个通过 map 方法把流中的每个元素转换成大写的例子。

List<String> list = new ArrayList<String>();Stream<String> stream = list.stream();Stream<String> streamMapped = stream.map((value) -> value.toUpperCase());

map 方法的参数直接是一个 Lambada 表达式:

(value) -> value.toUpperCase()

这个Lambda 表达式就是Function接口的实现。

函数式接口的载体通常是 Lambda 表达式,通过 Lambda 表达式,编译器会根据 Lambda 表达式的参数和返回值推断出其实现的是哪个函数式接口。使用 Lambda 表达式实现接口,我们不必像匿名内部类那样--指明类要实现的接口,所以像 Stream 操作中虽然参数或者返回值类型很多都是 Java 的内置函数式接口,但是我们并没有显示的使用匿名类实现它们。

虽然Lambda 表达式使用起来很方便,不过这也从侧面造成了咋一看到那些 Java 内置的函数式接口类型时,我们会有点迷惑“这货是啥?这货又是啥?”的感觉。

下面我们先说一下函数式编程、Java 的函数式接口、Lambda 为什么只能实现函数式接口这几个问题,把这些东西搞清楚了再梳理 Java 内置提供了哪些函数式接口。

函数式编程

函数式编程中包含以下两个关键的概念:

  • 函数是第一等公民

  • 函数要满足一下约束

    • 函数的返回值仅取决于传递给函数的输入参数。

    • 函数的执行没有副作用。

即使我们在写程序的时候没有一直遵循所有这些规则,但仍然可以从使用函数式编程思想编写程序中获益良多。

接下来,我们来看一下这两个关键概念再 Java 函数编程中的落地。

函数是一等公民

在函数式编程范式中,函数是语言中的第一等公民。这意味着可以创建函数的“实例”,对函数实例的变量引用,就像对字符串、Map 或任何其他对象的引用一样。函数也可以作为参数传递给其他函数。

在 Java 中,函数显然不是第一等公民,类才是。所以 Java 才引入 Lambda 表达式,这个语法糖从表现层上让 Java 拥有了函数,让函数可以作为变量的引用、方法的参数等等。为啥说是从表现层呢?因为实际上在编译的时候 Java 编译器还是会把 Lambda 表达式编译成类。

纯函数

函数编程中,有个纯函数(Pure Function)的概念,如果一个函数满足以下条件,才是纯函数:

  • 该函数的执行没有副作用。

  • 函数的返回值仅取决于传递给函数的输入参数。

下面是一个 Java 中的纯函数(方法)示例

public class ObjectWithPureFunction{    public int sum(int a, int b) {        return a + b;    }}

上面这个sum()方法的返回值仅取决于其输入参数,而且sum()是没有副作用的,它不会在任何地方修改函数之外的任何状态(变量)。

相反,这里是一个非纯函数的例子:

public class ObjectWithNonPureFunction{    private int value = 0;    public int add(int nextValue) {        this.value += nextValue;        return this.value;    }}

add()方法使用成员变量value来计算其返回值,并且它还修改了value成员变量的状态,这代表它有副作用,这两个条件都导致add方法不是一个纯函数

正如我们看到的,函数式编程并不是解决所有问题的银弹。尤其是“函数是没有副作用的”这个原则就使得在一些场景下很难使用函数式编程,比如要写入数据库的场景,写入数据库就算是一个副作用。所以,我们需要做的是了解函数式编程擅长解决哪些问题,把它用在正确的地方。

函数式接口

Java中的函数式接口在 Lambda 表达式那篇文章里提到过,这里再详细说说。函数式接口是只有一个抽象方法的接口(抽象方法即未实现方法体的方法)。一个 Interface 接口中可以有多个方法,其中默认方法和静态方法都自带实现,但是只要接口中有且仅有一个方法没有被实现,那么这个接口就可以被看做是一个函数式接口

下面这个接口只定义了一个抽象方法,显然它是一个函数式接口:

public interface MyInterface {    public void run();}

下面这个接口中,定义了多个方法,不过它也是一个函数式接口:

public interface MyInterface2 {    public void run();    public default void doIt() {        System.out.println("doing it");    }    public static void doItStatically() {        System.out.println("doing it statically");    }}

因为doIt方法在接口中定义了默认实现,静态方法也有实现,接口中只有一个抽象方法run没有提供实现,所以它满足函数式接口的要求。

这里要注意,如果接口中有多个方法没有被实现,那么接口将不再是函数式接口,因此也就没办法用 Java 的 Lambda 表达式实现接口了

编译器会根据 Lambda 表达式的参数和返回值类型推断出其实现的抽象方法,进而推断出其实现的接口,如果一个接口有多个抽象方法,显然是没办法用 Lambda 表达式实现该接口的。

@FunctionalInterface 注解

这里扩充一个标注接口是函数式接口的注解@FunctionalInterface

@FunctionalInterface // 标明接口为函数式接口public interface MyInterface {    public void run(); //抽象方法}

一旦使用了该注解标注接口,Java 的编译器将会强制检查该接口是否满足函数式接口的要求:“确实有且仅有一个抽象方法”,否则将会报错。

需要注意的是,即使不使用该注解,只要一个接口满足函数式接口的要求,那它仍然是一个函数式接口,使用起来都一样。该注解只起到--标记接口指示编译器对其进行检查的作用。

Java 内置的函数式接口

Java 语言内置了一组为常见场景的用例设计的函数式接口,这样我们就不必每次用到Lambda 表达式、Stream 操作时先创建函数式接口了,Java 的接口本身也支持泛型类型,所以基本上 Java 内置的函数式接口就能满足我们平时编程的需求,我自己在开发项目时,印象里很少见过有人自定义函数式接口。

Function

Function接口(全限定名:java.util.function.Function)是Java中最核心的函数式接口。 Function 接口表示一个接受单个参数并返回单个值的函数(方法)。以下是 Function 接口定义的:

@FunctionalInterfacepublic interface Function<T, R> {    R apply(T t);    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {        Objects.requireNonNull(before);        return (V v) -> apply(before.apply(v));    }    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {        Objects.requireNonNull(after);        return (T t) -> after.apply(apply(t));    }    static <T> Function<T, T> identity() {            return t -> t;    }

Function接口本身只包含一个需要实现的抽象方法apply,其他几个方法都已在接口中提供了实现,这正好符合上面我们讲的函数式接口的定义:“有且仅有一个抽象方法的接口”。

Function 接口中的其他三个方法中compseandThen 这两个方法用于函数式编程的组合调用,identity用于返回调用实体对象本身。

Function接口用Java 的类这么实现

public class AddThree implements Function<Long, Long> {    @Override    public Long apply(Long aLong) {        return aLong + 3;    }    public static void main(String[] args) {        Function<Long, Long> adder = new AddThree();Long result = adder.apply(4L);System.out.println("result = " + result);    }}

不过现实中没有这么用的,前面说过 Lambda 表达式是搭配函数式接口使用的,用Lambda表达式实现上Function 接口只需要一行,上面那个例子用 Lambda 实现的形式是:

Function<Long, Long> adder = (value) -> value + 3;Long resultLambda = adder.apply(8L);System.out.println("resultLambda = " + resultLambda);

是不是简洁了很多。后面的接口示例统一用 Lambda 表达式举例,不再用类实现占用太多篇幅。

Function接口的常见应用是 Stream api 中的 map 操作方法,该方法的参数类型是Function接口,表示参数是一个“接收一个参数,并返回一个值的函数”。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

所以我们在代码里常会见到这样使用 map 操作:

stream.map((value) -> value.toUpperCase())

Predicate

Predicate 接口 (全限定名:java.util.function.Predicate)表示一个接收单个参数,并返回布尔值 true 或 false 的函数。以下是 Predicate 功能接口定义:

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

Predicate 接口里还有几个提供了默认实现的方法,用于支持函数组合等功能,这里不再赘述。用 Lambda 表达式实现 Predicate 接口的形式如下:

Predicate predicate = (value) -> value != null;

Stream API 中的 filter 过滤操作,接收的就是一个实现了 Predicate 接口的参数。

Stream<T> filter(Predicate<? super T> predicate);

写代码时,会经常见到这样编写的 filter 操作:

Stream<String> longStringsStream = stream.filter((value) -> {    // 元素长度大于等于3,返回true,会被保留在 filter 产生的新流中。    return value.length() >= 3;});

Supplier

Supplier 接口(java.util.function.Supplier),表示提供某种值的函数。其定义如下:

@FunctionalInterfacepublic interface Supplier<T> {    T get();}

Supplier接口也可以被认为是工厂接口,它产生一个泛型结果。与 Function 不同的是,Supplier 不接受参数。

Supplier<Integer> supplier = () -> new Integer((int) (Math.random() * 1000D));

上面这个 Lambda 表达式的 Supplier 实现,用于返回一个新的 Integer 实例,其随机值介于 0 到 1000 之间。

Consume

Consumer 接口(java.util.function.Consume)表示一个函数,该函数接收一个参数,但是不返回任何值。

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

Consumer 接口常用于表示:要在一个输入参数上执行的操作,比如下面这个用Lambda 表达式实现的 Consumer,它将作为参数传递给它的value变量的值打印到System.out标准输出中。

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

Stream API 中的 forEach、peek 操作方法的参数就是 Consumer 接口类型的。

Stream<T> peek(Consumer<? super T> action);void forEach(Consumer<? super T> action);

比如,Stream API 中的 forEach 操作,会像下面这样使用 Consume 接口的实现

Stream<String> stream = stringList.stream();// 下面是Lambda 的简写形式// 完整形式为:value -> System.out.println(value);stream.forEach(System.out::println);

Optional

Optional 接口并不是一个函数式接口,这里介绍它主要是因为它经常在一些 Stream 操作中出现,作为操作的返回值类型,所以趁着学习函数式编程的契机也学习一下它。

Optional 接口是预防NullPointerException的好工具,它是一个简单的容器,其值可以是 null 或非 null。比如一个可能返回一个非空结果的方法,方法在有些情况下返回值,有些情况不满足返回条件返回空值,这种情况下使用 Optional 接口作为返回类型,比直接无值时返回 Null 要更安全。接下来我们看看 Optional 怎么使用:

// of 方法用于构建一个 Optional 容器Optional<String> optional = Optional.of("bam");// 判断值是否为空optional.isPresent();           // true// 取出值,如果不存在直接取会抛出异常optional.get();                 // "bam"// 取值,值为空时返回 orElse 提供的默认值optional.orElse("fallback");    // "bam"// 如果只存在,执行ifPresent参数中指定的方法optional.ifPresent((s) -> System.out.println(s.charAt(0)));// "b"

Stream 操作中像 findAny、 findFirst这样的操作方法都会返回一个 Optional 容器,意味着结果 Stream 可能为空,因此没有返回任何元素。我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。

以上就是“Java函数式编程怎么应用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: Java函数式编程怎么应用

本文链接: https://lsjlt.com/news/345567.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java函数式编程怎么应用
    今天小编给大家分享一下Java函数式编程怎么应用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Java 根据常用需求场景的用...
    99+
    2023-07-04
  • JavaScript中的函数式编程怎么应用
    本文小编为大家详细介绍“JavaScript中的函数式编程怎么应用”,内容详细,步骤清晰,细节处理妥当,希望这篇“JavaScript中的函数式编程怎么应用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。JavaS...
    99+
    2023-06-27
  • golang函数的函数式编程应用
    在 go 中,函数式编程通过 lambda 表达式、高阶函数和函数组合实现。lambda 表达式允许定义匿名函数,高阶函数接受函数作为输入或返回值,函数组合可以组合多个函数创建新函数。实...
    99+
    2024-04-28
    golang 函数式编程 字符串数组
  • Java中的函数式编程怎么使用
    本文小编为大家详细介绍“Java中的函数式编程怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java中的函数式编程怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。概述背景函数式编程的理论基础是阿隆...
    99+
    2023-07-05
  • Java中怎么支持函数式编程
    这篇文章将为大家详细讲解有关Java中怎么支持函数式编程,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java一直是面向对象的语言,一切皆对象,如果想要调用一个函数,函数必须属于一个类或对象...
    99+
    2023-06-16
  • Java8函数式编程之收集器怎么应用
    这篇文章主要介绍“Java8函数式编程之收集器怎么应用”,在日常操作中,相信很多人在Java8函数式编程之收集器怎么应用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java8函数式编程之收集器怎么应用”的疑...
    99+
    2023-07-06
  • java函数式接口怎么应用
    这篇文章主要介绍了java函数式接口怎么应用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java函数式接口怎么应用文章都会有所收获,下面我们一起来看看吧。一、函数式接口概念函数式接口在Java中是指:有且仅有...
    99+
    2023-07-05
  • 怎么在Java中实现函数式编程
    本篇文章为大家展示了怎么在Java中实现函数式编程,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一种基于Java的实现了...
    99+
    2023-06-14
  • Java 8中怎么实现函数式编程
    这期内容当中小编将会给大家带来有关Java 8中怎么实现函数式编程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。我被 Stack Overflow 上网友“mip”提的一个有趣的问题给难住了。该问题是:1...
    99+
    2023-06-17
  • Java中怎么实现Lambda函数式编程
    Java中怎么实现Lambda函数式编程,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。背景java 8 Lambda函数式编程,像阿里、腾讯这样的大互联网公司早就已经使用的技术...
    99+
    2023-06-17
  • Java中的函数式编程
    目录1、Lambda2、函数接口2.1 函数描述符3、Java函数接口3.1 Predicate3.2 Consumer 3.3 Function3.4 Supplier3.5 Pr...
    99+
    2024-04-02
  • Java 函数式编程梳理
    目录一、Lambda表达式 1.1 函数式编程思想概述1.2 体验Lambda表达式1.3 Lambda表达式的标准格式1.4 Lambda表达式的练习1.5 Lambda...
    99+
    2024-04-02
  • Lambda 的实战指南:Java 中的函数式编程应用
    Lambda 表达式是 Java 8 中引入的一项重要特性,它允许开发人员将函数作为参数传递。这使得在 Java 中实现函数式编程成为可能,函数式编程是一种强调不变性、纯净性和不可变性的编程范式。 优点 使用 Lambda 表达式提供了以...
    99+
    2024-04-02
  • Java响应式编程之handle怎么使用
    这篇文章主要介绍“Java响应式编程之handle怎么使用”,在日常操作中,相信很多人在Java响应式编程之handle怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java响应式编程之handle怎...
    99+
    2023-07-04
  • Java中怎么实现数据流和函数式编程
    本篇文章为大家展示了Java中怎么实现数据流和函数式编程,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。基础特性流 API 是在数据序列中迭代元素的简洁而高级的方法。包 java.util.strea...
    99+
    2023-06-16
  • Java 函数式编程与 Lambda 表达式
    文章目录 函数式编程与Lambda表达式 背景介绍 文章目的 目标受众 技术原理及概念 基本概念解释 相关技术比较 ...
    99+
    2023-10-09
    java scala 开发语言
  • Java 函数式编程要点总结
    目录一、函数式概念二、函数与方法三、JDK函数基础1、Lambda表达式2、函数式接口四、Optional类1、Null判断2、Optional应用五、Stream流六、源代码地址一...
    99+
    2024-04-02
  • 【Java系列】函数式接口编程
    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,...
    99+
    2023-12-23
    java 开发语言
  • Golang函数式编程在数据处理中的应用
    在 go 中利用函数式编程处理数据的好处:不可变性:防止意外修改输入数据,提高代码可靠性。纯函数:输出仅取决于输入,无副作用,便于测试和推理。一等函数:函数可作为参数或返回值,增强代码灵...
    99+
    2024-04-13
    golang 函数式编程 c# 代码可读性
  • 怎么使用JS函数式编程实现XDM
    这篇文章主要讲解了“怎么使用JS函数式编程实现XDM”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用JS函数式编程实现XDM”吧!FP 概览重要性函数式编程(FP),不是一个新的概念,...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作