目录

  • Chapter 13. Thinking functionally
  • Chapter 14. Functional programming techniques
  • Chapter 15. comparing Java 8 and Scala
  • Chapter 16. Conclusions

Chapter 13. Thinking functionally

13.1 实现和维护系统

有synchronized关键字的不要维护
容易使用的程序

  • Stream的无状态的行为(函数不会由于需要等待从另一个方法中读取变量,或者由于需要写入的变量同时有另一个方法正在写入,而发生中断)让我们
  • 最好类的 结构应该反映出系统的结构
  • 提供指标对结构的合理性进行评估,比如耦合性(软件系统中各组件之间是否相互独立)以及内聚性(系统的各相关部分之间如何协作)

不过对于日常事务,最关心的是代码维护时的调试:代码遭遇一些无法预期的值就有可能发生崩溃。这些无法预知的变量都源于共享的数据结构被你所维护的代码中的多个方法读取和更新。

对此,函数式编程提出的“无副作用”以及“不变性”

无副作用
函数:如果一个方法既不修改它内嵌类的状态,也不修改其他对象的状态,使用return返回所有的计算结果,那么我们称其为无副作用的。函数如果抛出异常,I/O和对类中的数据进行任何修改(除构造器内的初始化)都是有副作用的。

  • 变量:final型

声明式编程
一般通过编程实现一个系统,有两种思考方式。一种专注于如何实现,另一种方式则更加关注要做什么。
前一种为经典的面向对象编程,命令式;后一种为内部迭代,声明式。
第二种方式编写的代码更加接近问题陈述。

函数式编程实现了上述的两种思想:使用不相互影响的表达式,描述想要做什么(由系统来选择如何实现)。

13.2 函数式编程

1.函数式Java编程
Java语言无法实现纯粹函数式(完全无副作用)的程序,只能接近(副作用不会被察觉)。
这种函数只能修改局部变量,它的引用对象(参数及其他外部引用)都是不可修改对象(复制后再使用非函数式行为,如add)。除此之外,不抛异常(用Optional,或者局部抛异常),不进行I/O。

2.引用透明性(上面规定的隐含)
一个函数只要传递同样的参数值,它总是返回同样(==)的结果。

3.例子

//给定一个List<value>,返回其子集,类型为List<List<Integer>>,下面是整体算法,下下面是函数式的实践
static List<List<Integer>> subsets(List<Integer> list)if (list.isEmpty()) {List<List<Integer>> ans = new ArrayList<>();ans.add(Collections.emptyList());return ans;}Integer first = list.get(0);List<Integer> rest = list.subList(1,list.size());List<List<Integer>> subans = subsets(rest);List<List<Integer>> subans2 = insertAll(first, subans);return concat(subans, subans2);//
static List<List<Integer>> insertAll(Integer first,List<List<Integer>> lists) {List<List<Integer>> result = new ArrayList<>();for (List<Integer> list : lists) {List<Integer> copyList = new ArrayList<>();//复制新list,而不是直接用参数调用.addcopyList.add(first);copyList.addAll(list);result.add(copyList);}return result;
}
//下面方式相同
static List<List<Integer>> concat(List<List<Integer>> a,List<List<Integer>> b) {List<List<Integer>> r = new ArrayList<>(a);r.addAll(b);return r;
}

13.3 递归和迭代

将增强for改为迭代器方式没有副作用?

Iterator<Apple> it = apples.iterator();while (it.hasNext()) {Apple apple = it.next();// ...
}

利用递归而非迭代来消除没步都需更新迭代变量(但是使用迭代在Java效率通常更差),如阶乘:

//下面代码除效率问题,还有StackOverflowError风险
static long factorialRecursive(long n) {return n == 1 ? 1 : n * factorialRecursive(n-1);
}//尾迭代能解决StackOverflowError问题。每次调用函数时,把新的结果传入函数。遗憾Java目前还不支持这种优化,Scala可以。
static long factorialTailRecursive(long n) {return factorialHelper(1, n);
}static long factorialHelper(long acc, long n) {return n == 1 ? acc : factorialHelper(acc * n, n-1);
}//Stream更简单
static long factorialStreams(long n){return LongStream.rangeClosed(1, n).reduce(1, (long a, long b) -> a * b);
}

总结:尽量使用Stream取代迭代操作,从而避免变化带来的影响。此外,如果递归能不带任何副作用地让你以更精炼的方式实现算法,你就应该用递归替换迭代,因为它更加易于阅读、实现和理解。大多数时候编程的效率要比细微的执行效率差异重要得多。

Chapter 14. Functional programming techniques

函数式语言更广泛的含义是:函数可以作参数、返回值,还能存储。
1.高阶函数
接受至少一个函数做参数,返回结果是一个函数

接收的作为参数的函数可能带来的副作用以文档的方式记录下来,最理想的情况下,接收的函数参数应该没有任何副作用。
2.科里化
一种将具备n个参数(比如,x和y)的函数f转化为使用m(m < n)个参数的函数g,并且这个函数的返回值也是一个函数,它会作为新函数的一个参数。后者的返回值和初始函数的 返回值相同。

//将下面函数科里化,即预设各种f和b的组合,需要使用时只需调用相应的函数加上确实的x。
static double converter(double x, double f, double b) {return x * f + b;
}//创建高阶函数
static DoubleUnaryOperator curriedConverter(double f, double b){ return (double x) -> x * f + b;
}
//其中一种组合
DoubleUnaryOperator convertUSDtoGBP = curriedConverter(0.6, 0);
//使用
double gbp = convertUSDtoGBP.applyAsDouble(1000);

14.2 持久化数据结构

这里指的不是数据库中的持久化
链表例子(火车旅行)

//TrainJourney类(火车站)有两个公有变量,price和onward(下一站),构造函数如下
public TrainJourney(int p, TrainJourney t) {price = p;onward = t;
}//link方法把两个单向链表(一列火车站)连成一体。下面代码在a的基础上连接,这样会破坏原来a的结构。如果a原本在其他地方有应用,那么那些地方也会受到影响。
static TrainJourney link(TrainJourney a, TrainJourney b){if (a==null) return b;TrainJourney t = a;while(t.onward != null){t = t.onward;}t.onward = b;return a;
}//函数式实现。下面的实现的结果是a的副本,后面接上b。所以要确保结果不被修改,否则b也会没修改。这也包括下面的tree例子
static TrainJourney append(TrainJourney a, TrainJourney b){return a==null ? b : new TrainJourney(a.price, append(a.onward, b));
}//函数式难免会有一定程度的复制,上面例子至少只复制了a,而不是在一个全新的list上连接a和b

树例子(个人信息)

//节点信息,如果强制遵守函数式编程,可以将下面变量声明为final
class Tree { private String key;private int val;private Tree left, right;public Tree(String k, int v, Tree l, Tree r) {key = k; val = v; left = l; right = r;}
} //函数式的节点更新,每次更新都会创建一个新tree,通常而言,如果树的深度为d,并且保持一定的平衡性,那么这棵树的节点总数是2^d
public static Tree fupdate(String k, int newval, Tree t) {return (t == null) ?new Tree(k, newval, null, null) :k.equals(t.key) ?new Tree(k, newval, t.left, t.right) :k.compareTo(t.key) < 0 ?new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) :new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right));
}

?实现部分函数式(某些数据更新对某些用户可见):

  • 典型方式:只要你使用非函数式代码向树中添加某种形式的数据结构,请立刻创建它的一份副本
  • 函数式:改动前,复制修改处之前的部分,然后接上剩余部分

14.3 Stream 的延迟计算

有一个延迟列表的实现例子
如果延迟数据结构能让程序设计更简单,就尽量使用它们。如果它们会带来无法接受的性能损失,就尝试以更加传统的方式重新实现它们。

14.4 模式匹配(Java暂未提供)

1.访问者设计模式
一个式子简化的代码,如5+0变为5,使用Expr.simplify。但一开始要对expr进行各种检查,如expr的类型,不同类型有不同变量,当符合条件才返回结果。这个过程涉及instanceof和cast等操作,比较麻烦。
而访问者设计模式能得到一定的简化,它需要创建一个单独的类(SimplifyExprVisitor),这个类封装了一个算法(下面的visit),可以“访问”某种数据 类型。

class BinOp extends Expr{String opname; Expr left, right;public Expr accept(SimplifyExprVisitor v){return v.visit(this);}
}public class SimplifyExprVisitor {...public Expr visit(BinOp e){if("+".equals(e.opname) && e.right instanceof Number && ...){return e.left;}return e;}
}//Java 中模式的判断标签被限制在了某些基础类型、枚举类型、封装基础类型的类以及String类型。
//Scala的简单实现
def simplifyExpression(expr: Expr): Expr = expr match {case BinOp("+", e, Number(0)) => ecase BinOp("*", e, Number(1)) => ecase BinOp("/", e, Number(1)) => ecase _ => expr
}

14.5 杂项

1.缓存或记忆表(并非函数式方案)

final Map<Range,Integer> numberOfNodes = new HashMap<>();
Integer computeNumberOfNodesUsingCache(Range range) {Integer result = numberOfNodes.get(range);if (result != null){return result;}result = computeNumberOfNodes(range);numberOfNodes.put(range, result);return result;
}

这段代码虽然是透明的,但并不是线程安全的(numberOfNodes可变)
2.结合器

Chapter 15. comparing Java 8 and Scala

下面默认先写Scala,或只写Scala

15.1 Scala 简介

1.你好

//命令式
object Beer {//单例对象def main(args: Array[String]/*Java先类型后变量*/){//不需要void,通常非递归方法不需要写返回类型;对象声明中的方法是静态的var n : Int = 2while( n <= 6 ){println(s"Hello ${n} bottles of beer")n += 1 }}
}//函数式
2 to 6 /*Int的to方法,接受Int,返回区间,即也可以用2.to(6)。后面foreach理解相同*/foreach { n => println(s"Hello ${n} bottles of beer") }

同样一切为对象,但没有基本类型之分
Scala中用匿名函数或闭包指代lambda

2.数据结构
Map:
val authorsToAge = Map("Raoul" -> 23)Java需要创建后put
val authors = List("Raoul", "Mario")

Scala中的集合默认都是持久化的:更新一个Scala集合会生成一个新的集合,这个新的集合和之前版本的集合共享大部分的内容,最终的结果是数据尽可能地实现了持久化。由于这一属性,代码的隐式数据依赖更少:人们对代码中集合变更的困惑(比如在何处更新了集合,什么时候做的更新)也会更少。
val newNumbers = numbers + 8numbers为Set,添加元素是创建一个新Set对象

Java的不可变(immutable)比不可修改(unmodifiable)更彻底

val fileLines = Source.fromFile("data.txt").getLines.toList()
val linesLongUpper= fileLines.filter(l => l.length() > 10).map(l => l.toUpperCase())
//另一种表达,多加.par表示并行
fileLines.par filter (_.length() > 10) map(_.toUpperCase())

元组
val book = (2014, "Java 8 in Action", "Manning")可不同类型,任意长度(上限23)
Java需要自己建pair类,�且3个以上元素的pair比较麻烦

Stream
Scala中可以访问之前计算的值,可以通过索引访问,同时内存效率会变低。

Option
和Java很像

def getCarInsuranceName(person: Option[Person], minAge: Int) = person.filter(_.getAge() >= minAge).flatMap(_.getCar).flatMap(_.getInsurance).map(_.getName).getOrElse("Unknown")

15.2 函数

Scala多了“能够读写非本地变量”和对科里化的支持
1.一等函数

//filter的函数签名
def filter[T](p: (T) => Boolean/*Java用函数式接口Predicate<T>或 者Function<T, Boolean>,Scala直接用函数描述符或名为函数类型*/): List[T]//参数类型//定义函数
def isShortTweet(tweet: String) : Boolean = tweet.length() < 20
//使用函数,tweets是List[String]
tweets.filter(isShortTweet).foreach(println)

2.匿名函数和闭包

//上面代码的匿名方式如下(都是语法糖)
val isLongTweet : String => Boolean= (tweet : String) => tweet.length() > 60isLongTweet("A very short tweet")//Java的匿名方式
Function<String, Boolean> isLongTweet = (String s) -> s.length() > 60;boolean long = isLongTweet.apply("A very short tweet");//闭包
var count = 0
val inc = () => count+=1
inc()
println(count)
//Java
int count = 0;
Runnable inc = () -> count+=1;//会出错,count必须为final或效果为final
inc.run();

3.科里化
Java需要手工地切分函数,麻烦在于多参数情况

//Java
static Function<Integer, Integer> multiplyCurry(int x) {return (Integer y) -> x * y;
}Stream.of(1, 3, 5, 7).map(multiplyCurry(2)).forEach(System.out::println);//Scala
def multiplyCurry(x :Int)(y : Int) = x * yval multiplyByTwo : Int => Int = multiplyCurry(2)
val r = multiplyByTwo(10)

15.3 类和trait

1.类
Scala中的getter和setter都是隐式实现的

class Student(var name: String, var id: Int)val s = new Student("Raoul", 1)
println(s.name)//getter
s.id = 1337//setter

2.trait
与interface类似,有抽象方法、默认方法、接口多继承。但trait还有抽象类的字段。Java支持行为的多继承,但还不支持对状态的多继承。
Scala可以在类实例化时才决定trait

val b1 = new Box() with Sized

Chapter 16. Conclusions

Java8的发展体现了两种趋势:多核处理的需求(独立CPU速度瓶颈)->并行计算;更简洁地对抽象数据进行操作。
1.行为参数化:Lambda和方法引用
2.对大量数据的处理Stream:能在一次遍历中完成多种操作,而且按需计算。
并行处理中的重点:无副作用、Lambda、方法引用、内部迭代
3.CompletableFuture提供了像thenCompose、thenCombine、allOf这样的操作,避免Future中的命令式编程
4.Optional:能显式表示缺失值。正确使用能够发现数据缺失的原因。还有一些与Stream类似的方法。
5.默认方法

转载于:https://www.cnblogs.com/code2one/p/9871621.html

Java 8 实战 P4 Beyond Java 8相关推荐

  1. Java开发实战讲解!java象棋游戏源代码

    前言 "金九银十"的秋招热潮已经开始了,经过7月8月这两个月的提前批,终于成功拿下了一些大厂的offer.小编经过这么多次的面试,这两天整理了一份面试清单分享给大家,希望能给大家一 ...

  2. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)

    MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...

  3. java课程实战培训,终获offer

    正文 梳理知识点,是快速提升技术的关键 前面讲过,快速提升自己的技术硬实力其实是有方法的.大致就是梳理知识点+夯实基础+进阶深入学习+实战,下面我会一点点跟大家剖析,本文干货满满,大家仔细阅读. 梳理 ...

  4. 【Java】实战Java虚拟机之五“开启JIT编译”

    今天开始实战Java虚拟机之五"开启JIT编译" 总计有5个系列 实战Java虚拟机之一"堆溢出处理" 实战Java虚拟机之二"虚拟机的工作模式&qu ...

  5. Java 10 实战第 1 篇:局部变量类型推断

    现在 Java 9 被遗弃了直接升级到了 Java 10,之前也发过 Java 10 新特性的文章,现在是开始实战 Java 10 的时候了. 今天要实战的是 Java 10 中最重要的特性:局部变量 ...

  6. java并发实战编程pdf_「原创」Java并发编程系列25 | 交换器Exchanger

    2020年Java面试题库连载中 [000期]Java最全面试题库思维导图 [001期]JavaSE面试题(一):面向对象 [002期]JavaSE面试题(二):基本数据类型与访问修饰符 [003期] ...

  7. Java Socket实战之四 传输压缩对象

    2019独角兽企业重金招聘Python工程师标准>>> 本文地址:http://blog.csdn.net/kongxx/article/details/7259834 Java S ...

  8. 递归算法介绍及Java应用实战

    转载自 递归算法介绍及Java应用实战 什么是递归算法 递归算法是把问题转化为规模缩小了的同类问题的子问题,然后递归调用函数(或过程)来表示问题的解.一个过程(或函数)直接或间接调用自己本身,这种过程 ...

  9. Java并发编程实战_福州java编程实战培训班排名

    如何选择福州java培训中心? 在福州,如果想迅速掌握java开发,参加福州java培训班无疑是一种非常有效的方式.但是,市场上有这么多的java培训机构,我们在选择的时候难免会眼花缭乱.福州java ...

最新文章

  1. Windows Server 2012关闭Server Manager开机自启动
  2. 学嵌入式Linux软件开发需要的知识
  3. python效率提升_Python GUI开发,效率提升10倍的方法!
  4. mysql 一对多 关联一条最新的数据_不得不会的mysql锁
  5. Pro Silverlight 5 in C# 分享
  6. Linux 免密登录和配置环境变量
  7. object string java_java实现Object转String的4种方法小结
  8. 雷军100亿押注IoT,小米借AI两翼齐飞
  9. Windows 8 Directx 开发学习笔记(九)材质定义及混合光照效果实现
  10. memcached+magent实现负载
  11. redis增加auth
  12. Visual Studio Node.js工具1.1
  13. SRv6技术课堂(一):SRv6概述
  14. 为了让你在“口袋奇兵”聊遍全球,java面试代码题
  15. 计算机网络——网络聊天程序的设计与实现
  16. 【附源码】计算机毕业设计java学习资源共享网站设计与实现
  17. android 监听手机屏幕唤醒和睡眠广播
  18. 西安80转2000坐标参数_ERDAS中自定义坐标系的方法(转自百度,仅供参考)
  19. 黑盒测试和白盒测试是软件测试的两种基本方法,请分别说明各自的优点和缺点!
  20. Oracle中的emp、dept、bonus及salgrade表的新建及MySQL中的三种注释形式

热门文章

  1. harmonyos能装app吗,真机运行HarmonyOS应用APP
  2. python 上传文件夹,python – 使用Flask上传文件夹/文件
  3. 字节跳动学习笔记:java多线程实现原理
  4. 【深度学习】基于Pytorch进行深度神经网络计算(一)
  5. 【深度学习入门到精通系列】神经进化 (NeuroEvolution)
  6. java封装,继承和多态
  7. tc溜溜865手机投屏卡_这台手机智商为零,却要挑战小米华为!
  8. electron 两个窗口如何通信_关于 Electron 进程间通信的一个小小实践
  9. 边缘计算架构_多接入边缘计算框架与参考架构简介
  10. 服务器内存一般多大_各类网站服务器内存多大才合适?