Java函数式编程详解

Java函数式编程详解Java 从 1 8 以后引入了函数式编程 这是很大的一个改进 函数式编程的优点在提高编码的效率 增强代码的可读性 本文历时两个多月一点点写出来 即作为心得 亦作为交流 1 Java 函数式编程的语法 使用 Consumer 作为示例 它是一个函数式接口 包含一个抽象方法 accept 这个方法只有输入而无输出也就是说这个方法无返回值 现在我们要定义一个 Consumer 接口的实例化对象 传统的方式是

Java从1.8以后引入了函数式编程,这是很大的一个改进。函数式编程的优点在提高编码的效率,增强代码的可读性。本文历时两个多月一点点写出来,即作为心得,亦作为交流。

1.Java函数式编程的语法:

这里可以打印出3.

上面是JDK1.8以前的做法,在1.8以后引入函数式的编程可以这样写:

上面的con、con1、con2这个Consumer对象的引用照样可以打印出3.

在上面的第二种写法是第一种写法的精简,但是只有在函数题中只有一条语句时才可以这么使用,做进一步的简化。

在上面的第三种写法是对第一、二种写法的进一步精简,由于第三种写法只是进行打印,调用了System.out中的println静态方法对输入参数直接进行打印。它表示的意思就是针对输入的参数将其调用System.out中的静态方法println进行打印。

上面已说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当作该抽象方法的实现。 
如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法的了。 
因此,=  后面的函数体我们就可以看成是accept函数的实现。

输入:-> 前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的,如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()。
函数体:->后面的部分,即被{}包围的部分;可以是一段代码。
输出:函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值。但是Consumer这个函数式接口不能有具体的返回值。

Java 8 中我们可以通过 `::` 关键字来访问类的构造方法,对象方法,静态方法。

2.Java函数式接口

JDK源码定义如下:

default Consumer

andThen(Consumer
after) {


        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };


 }

andThen这个方法是作用是:指定在调用当前Consumer后是否还要调用其它的Consumer; 

注意:当一个Consumer接口调用另外一个Consumer对象时两个Counsumer对象的泛型必须一致。

2.2  Function也是一个函数式编程接口;它代表的含义是“函数”,它是个接受一个参数并生成结果的函数,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出; 
除apply方法外,它还有compose与andThen及indentity三个方法,其使用见下述示例;

apply的用法:

上面打印出2。

Function编程接口有两个泛型Function

T表示:函数的输入类型,R表示:函数的输出类型。

compose的用法:

JDK源码定义:

default

Function

compose(Function
before) {


        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));



 }

上面打印出21;

上面表示了fun2接收到2以后先计算,然后将fun2计算的结果再交给fun再计算。

andThen方法的用法:

JDK源码定义:

default

Function

andThen(Function
after) {


        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));



 }

上面打印出40

上面表示了fun先计算得到4,然后将计算结果4再给fun1计算得到40;

indentity方法的用法:

JDK源码定义:

static

Function

identity() {


        return t -> t;
 }



 }

上面打印出“总分“;就是说传入什么参数输出什么参数。

2.3 Predicate为函数式接口,predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件; 因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。 

test方法的用法:

JDK源码定义: boolean test(T t);

上面打印出true。

and方法的用法:

JDK源码定义:

default Predicate

and(Predicate
other) {


        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
 }



根据源码,”1234″先和pre比较,如果为true,再和pre1比较。不为true直接返回false,不再和pre1比较。当“1234”和pre比较后为true,再和pre1比较结果为true,所以最终返回true。

negate()的用法:

JDK源码定义:

default Predicate

negate() {


        return (t) -> !test(t);
 }


上面打印出false,上面的意思是如果比较的结果是true,那么返回false,如果为false,就返回true。

or的方法的用法:

JDK源码定义:

default Predicate

or(Predicate
other) {


        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
}



上面打印出true;

or方法的意思是:只要一个为true就返回true。

isEqual方法用法:

JDK源码定义:

static

Predicate

isEqual(Object targetRef) {


        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
 }





}

上面打印出true;isEqual里的参数是要比较的目标对象。

3.函数式编程接口的使用

通过Stream以及Optional两个类,可以进一步利用函数式接口来简化代码。

3.1 Stream

Stream可以对多个元素进行一系列的操作,也可以支持对某些操作进行并发处理。

3.1.1 Stream对象的创建

Stream对象的创建途径有以下几种:

a. 创建空的Stream对象

Stream str = Stream.empty();

b. 通过集合类中的stream或者parallelStream方法创建;

List

list = Arrays.asList(“a”, “b”, “c”, “d”);

Stream listStream = list.stream();  //获取串行的Stream对象
Stream parallelListStream = list.parallelStream(); //获取并行的Stream对象  


c. 通过Stream中的of方法创建:

Stream s = Stream.of(“test”);

Stream s1 = Stream.of(“a”, “b”, “c”, “d”);

public static

Stream

iterate(final T seed, final UnaryOperator

f);  

public static

Stream

iterate(T seed, Predicate
hasNext, UnaryOperator

next)

其中第一个方法将会返回一个无限有序值的Stream对象:它的第一个元素是seed,第二个元素是f.apply(seed); 第N个元素是f.apply(n-1个元素的值);生成无限值的方法实际上与Stream的中间方法类似,在遇到中止方法前一般是不真正的执行的。因此无限值的这个方法一般与limit等方法一起使用,来获取前多少个元素。 
当然获取前多少个元素也可以使用第二个方法。 
第二个方法与第一个方法生成元素的方式类似,不同的是它返回的是一个有限值的Stream;中止条件是由hasNext来断定的。
第二种方法的使用示例如下:










Java函数式编程详解

下面就几个比较常用的方法举例说明其用法:

Stream

filter(Predicate
predicate);

使用示例:


Stream

s = Stream.of(“test”, “t1”, “t2”, “teeeee”, “aaaa”);

//查找所有包含t的元素并进行打印
s.filter(n -> n.contains(“t”)).forEach(System.out::println);

3.1.2.2 map
元素一对一转换。 
它接收一个Funcation参数,用其对Stream中的所有元素进行处理,返回的Stream对象中的元素为Function对原元素处理后的结果 
其方法定义如下:








Stream

map(Function
mapper);

示例,假设我们要将一个String类型的Stream对象中的每个元素添加相同的后缀.txt,如a变成a.txt,其写法如下:



Stream

s = Stream.of(“test”, “t1”, “t2”, “teeeee”, “aaaa”);

s.map(n -> n.concat(“.txt”)).forEach(System.out::println);

3.1.2.3 flatMap
元素一对多转换:对原Stream中的所有元素使用传入的Function进行处理,每个元素经过处理后生成一个多个元素的Stream对象,然后将返回的所有Stream对象中的所有元素组合成一个统一的Stream并返回; 
方法定义如下:






Stream

flatMap(Function
> mapper);

示例,假设要对一个String类型的Stream进行处理,将每一个元素的拆分成单个字母,并打印:



Stream

s = Stream.of(“test”, “t1”, “t2”, “teeeee”, “aaaa”);

s.flatMap(n -> Stream.of(n.split(“”))).forEach(System.out::println);

3.1.2.4 takeWhile
方法定义如下:




default Stream

takeWhile(Predicate
predicate)

如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。 
与Filter有点类似,不同的地方就在当Stream是有序时,返回的只是最长命中序列。 
如以下示例,通过takeWhile查找”test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”这几个元素中包含t的最长命中序列:




Stream

s = Stream.of(“test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”);

//以下结果将打印: “test”, “t1”, “t2”, “teeeee”,最后的那个taaa不会进行打印 
s.takeWhile(n -> n.contains(“t”)).forEach(System.out::println);

3.1.2.5 dropWhile
与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream;其定义如下:





default Stream

dropWhile(Predicate
predicate)

如以下示例,通过dropWhile删除”test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”这几个元素中包含t的最长命中序列:


Stream

s = Stream.of(“test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”);

//以下结果将打印:”aaaa”, “taaa”  
s.dropWhile(n -> n.contains(“t”)).forEach(System.out::println);

3.1.2.6 reduce与collect
关于reduce与collect由于功能较为复杂,在后续将进行单独分析与学习,此处暂不涉及。





3.2 Optional用于简化Java中对空值的判断处理,以防止出现各种空指针异常。 
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。

Optional

s = Optional.ofNullable(test());

s.ifPresent(System.out::println);

乍一看代码复杂度上差不多甚至是略有提升;那为什么要这么做呢? 
一般情况下,我们在使用某一个函数返回值时,要做的第一步就是去分析这个函数是否会返回空值;如果没有进行分析或者分析的结果出现偏差,导致函数会抛出空值而没有做检测,那么就会相应的抛出空指针异常! 
而有了Optional后,在我们不确定时就可以不用去做这个检测了,所有的检测Optional对象都帮忙我们完成,我们要做的就是按上述方式去处理。





Optional

o = Optional.ofNullable(s);

System.out.println(o.orElse(“test”));

3.2.3.3 变量为空时抛出异常,否则使用
以往写法:




Optional

o = Optional.ofNullable(s);

System.out.println(o.orElseThrow(()->new Exception(“test”)));
 


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/206426.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午3:43
下一篇 2026年3月19日 下午3:43


相关推荐

  • 串口服务器调试助手使用教程,串口服务器如何配置及串口调试6大技巧

    串口服务器调试助手使用教程,串口服务器如何配置及串口调试6大技巧串口服务器如何配置相信很多用户不是很清楚;今天就针对串口服务器如何配置以及串口调试的6大技巧,加以总结阐述:1、如何设置串行服务器的串行端口属性,例如波特率具体参数及数值大小?①点击屏幕上的“设备”单元;②手动打字输入“程序设置”;③再手动选择“程序”,最后手动输入“串行参数”。2、怎样配置串口服务器?首先,必须了解熟悉自身的操作环境与应用配置参数(熟悉每个串行端口的操作模式、熟悉主要参数包括的网…

    2022年5月6日
    185
  • 磁盘加密

    磁盘加密

    2022年3月12日
    46
  • 深度好文 | 深圳IT人力外包客户如何做好外包客户的征询?

    深度好文 | 深圳IT人力外包客户如何做好外包客户的征询?1、询问公司的人数、技术团队及技术人员分布情况。2、公司的项目情况。(产品型or项目型、项目简单介绍、项目所处阶段、项目配备人员、预计开发周期、项目远期规划)3、采用外包的原因。(人员编制、项目紧急、招聘流程满、技术招聘能力不足、项目周期性、不愿养太多技术人员等)4、是否接触过外包、有无外包人员、最长外派多久了。5、职位需求、数量、周期、是否自备电脑。Java人员外包、web前端外包、Androi…

    2022年5月12日
    63
  • JMeter做压力测试教程及结果分析

    JMeter做压力测试教程及结果分析一、测试工具:JMeter二、JMeter介绍:ApacheJMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。三、Java环境的安装与配置:(1)因为JMeter是使用JAVA写的,所以使用JMeter之前,先安装JAVA环境,oracle官网下载JDkht……

    2022年10月18日
    4
  • laravel-admin 自定义导出excel功能,并导出图片

    laravel-admin 自定义导出excel功能,并导出图片

    2021年10月30日
    43
  • crunch字典生成

    crunch字典生成密码激活成功教程基本有三种方法 第一种是人工猜解 垃圾桶工程和被动信息收集 第二种是基于字典暴力激活成功教程 主流 在 kali 里 是默认自带了字典的 分别放在下面三个文件中 usr share wordlist usr share wfuzz wordlist usr share seclists 有一个 password 第三种是键盘空间字符爆破全键盘空间字符部分键盘空间字符 基于规则 数字 小写字母

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号