首页 > 编程语言 >[Java 8] (1) 函数式编程简介

[Java 8] (1) 函数式编程简介

时间:2023-01-06 10:04:32浏览次数:42  
标签:Java BigDecimal 简介 price 编程 接口 代码 函数

思维方式的转变

以从一个城市集合中寻找是否存在Chicago为例:
习惯的方式

boolean found = false ;
for ( String city : cities ) {
if ( city . equals ( "Chicago" )) {
found = true ;
break ;
}
}
System . out . println ( "Found chicago?:" + found );

以上代码就是绝大多数开发人员在面对这个问题时的第一反应。它通过命令式风格(Imperative Style)的代码来完成需要的逻辑,但是看起来会比较复杂,因为代码量较多。

稍有经验的开发人员则会利用现有的API来实现,使代码更简洁同时也更具有可读性,因为它将代码风格由命令式转变为声明式(Declarative Style)。

System . out . println ( "Found chicago?:" + cities . contains ( "Chicago" ));

简单的一行代码,就能够将程序的意图显示出来。
另一个例子
假设对于20元以上的商品,进行9折处理,最后得到这些商品的折后价格。如下解决方案立即会映入脑海:

final List < BigDecimal > prices = Arrays . asList (
new BigDecimal ( "10" ), new BigDecimal ( "30" ), new BigDecimal ( "17" ),
new BigDecimal ( "20" ), new BigDecimal ( "15" ), new BigDecimal ( "18" ),
new BigDecimal ( "45" ), new BigDecimal ( "12" ));

BigDecimal totalOfDiscountedPrices = BigDecimal . ZERO ;
for ( BigDecimal price : prices ) {
if ( price . compareTo ( BigDecimal . valueOf ( 20 )) > 0 )
totalOfDiscountedPrices = totalOfDiscountedPrices . add ( price . multiply ( BigDecimal . valueOf ( 0.9 )));
}
System . out . println ( "Total of discounted prices: " + totalOfDiscountedPrices );

当你经常性的写这种类型的代码时,不知道是否会产生一种无聊或者不安的情绪。因为这段代码已经普通到有点乏味了,虽然它能够正常工作,但是总会感觉到它并不是那么优雅。
更优雅的方式,是使用声明式的代码:

final BigDecimal totalOfDiscountedPrices = prices . stream ()
. filter ( price -> price . compareTo ( BigDecimal . valueOf ( 20 )) > 0 )
. map ( price -> price . multiply ( BigDecimal . valueOf ( 0.9 )))
. reduce ( BigDecimal . ZERO , BigDecimal: : add );

System . out . println ( "Total of discounted prices: " + totalOfDiscountedPrices );

没有声明任何的临时变量,没有各种if判断,逻辑一气呵成。同时也更具有可读性: 首先将价格集合根据条件进行过滤(filter),然后对过滤后的集合进行折扣处理(map),最后将折扣后的价格进行相加(reduce)。
它利用了Java 8的新特性,Lambda表达式以及相关的方法如stream(),reduce()等将代码转变成函数式的风格(Functional Style)。Lambda表达式和其相关内容会在后文中进行详细介绍。
使用函数式代码的好处

减少了可变量(Immutable Variable)的声明
能够更好的利用并行(Parallelism)
代码更加简洁和可读

当然,Java 8中对于函数式编程风格的引入,并不是为了要颠覆已经根深蒂固面向对象编程风格。而是让它们和谐共处,取长补短。比如,使用面向对象对实体进行建模,对实体之间的关系进行表述;而使用函数式编码来实现实体中的各种状态改变,业务流程以及数据处理。
函数式编程的核心

声明式的代码风格(Declarative Style) : 这需要提高代码的抽象层次,比如在前面的例子中,将从集合中搜索一个元素的操作封装到contains方法中。

更多的不变性(Promote Immutability) : 能不声明变量就不要声明,需要变量时尽量使用final来修饰。因为变量越多,就意味着程序越难以并行。实现了不变性的方法意味着它不再有副作用,不会因为调用而改变程序的状态。

使用表达式来代替语句(Prefer Expression to Statement) : 使用语句也就意味着不变性的破坏和程序状态的改变,比如赋值语句的使用。

使用高阶函数(High-Order Function) : 在Java 8以前,重用是建立在对象和类型系统之上。而Java 8中则将重用的概念更进一步,使用函数也能够实现代码的重用。所谓高阶函数,不要被其名字唬住了,实际上很简单:

将函数作为参数传入到另外一个函数中
函数的返回值可以是函数类型
在函数中创建另一个函数

在前文中,已经见过函数作为参数传入到另一个函数的例子了:

prices . stream ()
. filter ( price -> price . compareTo ( BigDecimal . valueOf ( 20 )) > 0 )
. map ( price -> price . multiply ( BigDecimal . valueOf ( 0.9 )))
. reduce ( BigDecimal . ZERO , BigDecimal: : add );

price -> price.multiply(BigDecimal.valueOf(0.9))实际上就是一个函数。只不过它的写法使用了Lambda表达式,当代码被执行时,该表达式会被转换为一个函数。
函数式接口(Functional Interface)
为了在Java中引入函数式编程,Java 8中引入了函数式接口这一概念。
函数式接口就是仅声明了一个方法的接口,比如我们熟悉的Runnable,Callable,Comparable等都可以作为函数式接口。当然,在Java 8中,新添加了一类函数式接口,如Function,Predicate,Consumer,Supplier等。
在函数式接口中,可以声明0个或者多个default方法,这些方法在接口内就已经被实现了。因此,接口的default方法也是Java 8中引入的一个新概念。
函数式接口使用@FunctionalInterface注解进行标注。虽然这个注解的使用不是强制性的,但是使用它的好处是让此接口的目的更加明确,同时编译器也会对代码进行检查,来确保被该注解标注的接口的使用没有语法错误。
如果一个方法接受一个函数式接口作为参数,那么我们可以传入以下类型作为参数:

匿名内部类(Anonymous Inner Class)
Lambda表达式
方法或者构造器的引用(Method or Constructor Reference)

第一种方式是Java的以前版本中经常使用的方式,在Java 8中不再被推荐。 第二种方式中,Lambda表达式会被编译器转换成相应函数式接口的一个实例。

标签:Java,BigDecimal,简介,price,编程,接口,代码,函数
From: https://blog.51cto.com/u_15733182/5992165

相关文章

  • Python网络编程之TCP服务端程序开发
     一、开发TCP服务端程序开发步骤回顾创建服务端端套接字对象绑定端口号设置监听等待接受客户端的连接请求接收数据发送数据关闭套接字二、socket类的介绍导入socket模......
  • JAVA常用的工具类
    1集合工具类1.1java.util.Collections使用的基本list示意List<Integer>list=newArrayList<>();list.add(2);list.add(1);list.add(3);1.1.1基本操作Collections.so......
  • JAVA调用SAP RFC接口
    java要调用SAPRFC接口时,需要用到sapjco3.jar架包;windows下还需要将文件sapjco3.dll文件放到system32的目录下;linux下同样需要把sapjco3.so放入项目的执行目录下;下载......
  • JAVA获取上下行网速
    packagecom.iecas.zwterminalstate.util;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.util.Formatter;imp......
  • 【Java】线程池梳理
    【Java】线程池梳理前言线程池:本质上是一种对象池,用于管理线程资源。在任务执行前,需要从线程池中拿出线程来执行。在任务执行完成之后,需要把线程放回线程池。通过线程的......
  • java -jar 指定logback.xml、application.yaml
    java-jar指定logback.xml -Dlogging.config="C:\logbacs\logback.xml"示例:java-jar  -Dlogging.config="/home/yqq/config/logback.xml" zwdatagather-1.0.0......
  • docker简介和基础概念
    docker是什么docker基于linux内核的cgroup、namespace和unionfs等技术,对进程实行封装隔离,属于os层面的虚拟化技术。由于进程的隔离独立于宿主机和其他隔离的进程,因此也......
  • Java面试题Day03
    1.HashMap的resize过程是什么样的?采用hash表数据加链表的形式,1.8以后引入了红黑树的数据结构,初始化数组长度为16,当数组长度到0.75时扩容,链表长度大于8时转为红黑树,......
  • 【一句话】JAVA8后abstract class和interface的区别
    首先一句话:JAVA8后(1)interface支持default和static方法有实现,abstractclass依然是抽象方法和非抽象方法,(2)可同时实现多个interface,(3)但成员变量只能是staticfanal的......
  • JAVA中使用最广泛的本地缓存?Ehcache的自信从何而来3 —— 本地缓存变身分布式集群缓存
    大家好,又见面了。本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面。如果感兴趣,欢迎关注以获取后续更新。上......