1.6 来自函数式编程的其他好思想
前几节介绍了Java从函数式编程引入的两个核心思想:将方法和Lambda作为一等值,以及在没有可变共享状态时,函数或方法可以有效、安全地并行执行。这两种思想新的Stream API都用到了。
常见的函数式语言,如SML、OCaml、Haskell,还提供了进一步的结构来帮助程序员,其中之一就是通过显式使用更多的描述性数据类型来避免null。确实,计算机科学巨擘之一托尼·霍尔(Tony Hoare)在2009年伦敦QCon上的演讲中说道:
我把它叫作我“价值亿万美元的错误”,就是在1965年发明了空引用……我无法抵抗放进一个空引用的诱惑,仅仅是因为它实现起来非常容易。
Java 8提供了一个Optional类,如果你能一致地使用它,就能帮助你避免出现NullPointerException。这是一个容器对象,它既可以包含值,也可以不包含值。Optional提供了方法来明确地处理值不存在的情况,这样就可以避免NullPointerException了。换句话说,它通过类型系统,允许你表明一个变量可能缺失值。第11章会详细讨论Optional。
第二个思想是(结构化的)模式匹配9。这个术语最早用在数学里,例如:
9这个术语有两个意思,这里指的是数学和函数式编程中的意思,即函数是分情况定义的,而不是使用if-then-else。它的另一个意思类似于“在给定目录中找到所有类似于IMG*.JPG形式的文件”,和所谓的正则表达式有关。
f(0) = 1f(n) = n*f(n-1) otherwise
Java中,你可以使用if-then-else或switch语句表达同样的语义。其他语言已经证实,对于更复杂的数据类型,在表达编程思想时,使用模式匹配比if-then-else更简明。你也可以采用多态和方法重写替代if-then-else来处理这种类型的数据,但是,到底哪种方式更适合,在语言设计上仍然有很多争论。10 我们认为两者都是有用的工具,你都应该掌握。不幸的是,Java 8并不完全支持模式匹配,我们会在第19章介绍如何用Java表达模式匹配。此外,还会介绍一个Java改进提议,讨论如何在未来的Java版本中支持模式匹配。与此同时,我们会用Scala语言(这是另一种基于JVM的类Java语言,它启发了Java的一些新特性,更多内容参见第20章)的一个例子进行介绍。譬如,你要设计一个程序,要对描述算术表达式的树做基本的简化。假设数据类型Expr代表了这个表达式,你可以用Scala编写如下代码,将Expr拆分为各个部分,然后返回一个新的Expr:
10维基百科中的文章“Expression Problem”(由Phil Wadler发明的术语)对这一讨论有所介绍。
def simplifyExpression(expr: Expr): Expr = expr match {case BinOp("+", e, Number(0)) => e ←---- 加上0case BinOp("-", e, Number(0)) => e ←---- 减去0case BinOp("*", e, Number(1)) => e ←---- 乘以1case BinOp("/", e, Number(1)) => e ←---- 除以1case _ => expr ←---- 不能简化expr}
这里,Scala的语法expr match就对应于Java中的switch (expr)。你暂时不用担心不理解这段代码,第19章会介绍更多关于模式匹配的内容。现在,你可以把模式匹配看作switch的扩展形式,它能够同时将一个数据类型分解成元素。
为什么Java中的switch语句要局限于原始类型值和Strings呢?函数式语言倾向于让switch支持更多的数据类型,甚至允许模式匹配(就像Scala语言中match的操作)。面向对象设计中,常用的访客模式可以用来遍历一组类(比如汽车的不同组件:车轮、发动机、底盘等),并对每个访问的对象执行操作。模式匹配的优势之一是编译器能够检测常见的错误,例如:“Brakes类是用来表示Car类的组件的一族类。你忘记了要显式处理它。”
第18章和第19章会全面介绍函数式编程,以及如何在Java 8中编写函数式风格的程序,包括库中提供的函数工具。第20章会讨论Java 8的功能并与Scala进行比较。Scala和Java一样基于JVM实现,且近年来发展迅速,已经在编程语言生态系统的一些方面威胁到了Java。这部分内容放在了本书的后面几章,你会进一步了解Java 8和Java 9为什么加上了这些新功能。
Java 8、9、10以及11的新特性:从哪里入手?
Java 8和Java 9都为Java语言提供了重大更新。不过,作为Java程序员,你更关心的可能是Java 8带来的变化,因为这将直接影响你的日常工作——传递方法或者Lambda表达式正变成日益重要的Java知识。与此相反,Java 9的改进提升的是我们定义和使用大型组件的能力,譬如使用模块化构建一个系统,或者导入一个反应式编程的工具集。最后,Java 10引入的变化比前面几个版本小得多,主要是新增了对局部变量类型推断的支持,第21章会详细探讨。此外,Java 11中Lambda表达式支持的参数语法会更丰富,第21章也会介绍。
截至本书创作时,Java 11的发布计划是2018年9月。Java 11还引入了一个全新的异步HTTP客户端库,它基于Java 8和Java 9提供的
CompletableFuture和反应式编程(详细内容参见第15章、第16章和第17章)。
