前言

本书将使用上千个示例代码分析和分解250多个Scala编程中常见的问题。

相较于其他Scala 3的学习资料而言,本书的独到之处在于:

·作为一本教程,本书旨在通过提供最常见问题的解决方案来帮助你提高效率。

·本书不仅涵盖了Scala语言本身,还介绍了Scala工具和库,包括sbt、Spark、Scala.js、Akka actor,以及用Play框架处理JSON。

·本书对Scala集合类进行了深入研究,用5章的篇幅展示了它们的用法。

·本书几乎所有的例子都在Scala解释器中演练。这样一来,不论你是在计算机旁、飞机上,还是在摇椅中阅读,都能看到各个例子的具体执行结果。

Scala 3语言

在本书的第1版中,我曾描述Scala 2感觉像是Ruby和Java的组合。当时我写道:“它像Ruby一样轻量、简洁、易读,但它又能编译成类文件进而打包成JAR文件,在Java虚拟机(JVM)上运行。它使用特质(trait)和混入(mixin),你可能感觉它是动态类型的,但它却是静态类型的。”

一直以来,Scala语言的特性在一个开放的环境中被广大开发人员不断思考和讨论。随着2021年Scala 3的发布,这门语言更加轻量了。现在,它似乎是4种优秀语言——Ruby、Java、Python和Haskell(轻量级、简洁的语法)的结合。

这种更轻量的感觉部分归功于新的可选大括号语法,这也被称为显著缩进方式。有了这一变化,以前的for循环是这样的:

现在是这样的:

同样,if表达式和许多其他表达式也提供了更简洁的语法,使得代码更容易阅读:

虽然这种新的语法被设计成可选的,但它已经成为事实上的标准,并被用于本书以及我和其他作者共同为Scala网站撰写的官方文档Scala 3 Bookhttps://oreil.ly/sUTXM)、Coursera上的Scala 3官方培训课程、Martin Odersky等人编写的Programming in Scala(Artima Press)一书和Dean Wampler编写的Programming Scala(O'Reilly)一书,以及其他资源。

除了新的语法外,Scala 3还新增了许多功能:

·枚举类型。

·并集类型和交集类型。

·顶层定义(因此代码不再需要包含在类、特质和对象中)。

·用新的given和using语法简化了implicit[1]的使用。

·大大简化了扩展方法和类型族的语法。

此外,特质和类的语法也被简化,变得更容易阅读:

在新的语法中,所有在代码中产生不必要“噪声”的结构都已被删除。

Scala功能

除了以上所述,Scala还提供了大量的功能,使其成为一种独特的、真正的现代编程语言:

·它是由Martin Odersky(javac之父)创建的,还受到了Java、Ruby、Smalltalk、ML、Haskell、Python、Erlang等语言的影响。

·它是一种高级编程语言。

·它有一个简洁、易读的语法,我们称之为表达式。

·它是静态类型的,因此你可以获得静态类型安全性的所有好处,同时使用起来却感觉像动态类型的脚本语言。

·它是一种纯粹的面向对象编程(Object-Oriented Programming,OOP)语言:每个变量都是一个对象,每个操作符都是一个方法。

·它同时也是一种函数式编程(Functional Programming,FP)语言,所以你可以将函数作为变量。

·事实上,正如Odersky先生所说的那样,Scala(https://oreil.ly/MOunk)的本质是FP和OOP在类型化环境中的融合,其中函数用于(编写)逻辑,对象用于(构建)模块化。

·它在JVM上运行,并且得益于Scala.js项目(https://www.scala-js.org),它也是一个类型安全的JavaScript替代品。

·它能与Java和其他JVM库无缝交互。

·得益于GraalVM和Scala Native项目,你现在可以从Scala代码中创建快速启动的原生可执行文件。

·新的Scala集合库带来了几十种预设的函数定义,可以节省你的时间,并大大减少编写自定义for循环和算法的需求。

·Scala中内置了编程的最佳实践,它倾向于不可更改性、匿名函数、高阶函数、模式匹配、默认情况下不能扩展的类等。

·Scala生态系统提供了最具现代化的函数式编程库。

我喜欢Scala的一点是,如果你熟悉Java,就可以在一天之内熟练掌握Scala。但这门语言很深邃,随着时间的推移,你可以不断学习并挖掘出更新、更好的方法来编写代码。Scala将改变你对编程的思考方式。

在Scala的所有优点中,我最喜欢的是它能让你写出简洁、易读的代码。据说,程序员阅读代码的时间与编写代码的时间的比例至少为10:1,因此编写简明易读的代码非常重要。

Scala轻巧灵动

Scala不仅表现力强,还能给人一种轻量、动态脚本语言的感觉。例如,Scala的类型推断系统消除了对显式声明的需求。你不必总是指定类型,而是可以简单地将变量分配给它们的数据:

注意,我们不需要显式声明变量是String、Int或Double类型。这是Scala的类型推断系统在发挥作用。

创建自定义类型的工作方式完全相同。给定一个Person类:

你可以创建一个“人”:

也可以使用必要的模板代码创建一个包含多个“人”的列表:

尽管我还没有引入for表达式,但我觉得任何有一点经验的开发人员都能理解这段代码:

而且,尽管我还没有引入枚举,但开发人员也应该知道这段代码的含义:

不知道大家有没有注意到,这里没有加入非必要的模板代码。代码既尽可能地“小马拉大车”,又易于阅读。我们非常注意延续Scala作为一种声明式语言的传统。

在所有这些例子中,你都可以看到Scala的轻量级语法,以及感受到它像一种动态脚本语言。

目标读者

本书旨在为使用Scala的程序员提供实用的参考,在使用Scala和其相关类库、工具遇到问题时,可以通过阅读本书快速找到答案。同时,我也希望本书成为其他语言背景的程序员学习Scala的一个好工具。我相信“实践出真知”,因此本书包含丰富的实践例子。

我假设读者对诸如C、C++、Java、Ruby、C#、PHP、Python、Haskell等其他编程语言有一定的经验。我本人也有使用这些语言的经验,因此我的写作也会受到它们的影响。

也可以通过软件开发者等级划分本书的目标读者。Martin Odersky在一篇关于Scala等级的文章(https://oreil.ly/QMV5U)中定义了软件开发者的等级:

·等级A1:初级应用程序员

·等级A2:中级应用程序员

·等级A3:专家级应用程序员

·等级L1:初级类库设计师

·等级L2:中级类库设计师

·等级L3:专家级类库设计师

本书的主要目标读者为等级A1、A2、A3的应用开发者和等级L1的类库设计师。同时我希望等级L2和L3的类库设计师也可以从本书中获益——尤其是那些没有函数式编程经验,或者希望跟上Scala及其他相关工具和类库的更新速度的开发者。

主要内容

第1章 包含一系列在命令行上使用Scala的例子。首先展示如何使用Scala REPL以及功能丰富的Ammonite REPL;然后介绍如何使用scalac和scala等命令行工具来编译和运行代码,以及如何使用javap命令来反编译Scala类文件;最后展示如何运行Scala生成的JAR文件。

第2章 介绍字符串的使用技巧。Scala中String的基本功能从Java继承而来,在强大的隐式转换的帮助下,Scala为原生的字符串增加了很多功能,你甚至可以把它当作一连串的字符值使用。

第3章 介绍Scala的数值类型,以及Java 8引入的日期类。你将会看到,在Scala语言中,数值类型不支持++或者--操作符。该章将解释其原因,并演示其他可以使用的方法。还将展示如何处理大数值、货币,以及如何比较浮点数。在日期的例子中,使用Java 8的日期类,并且还会展示如何使用遗留的日期类型。

第4章 介绍Scala内建的控制结构。首先解释if/then语句、基础的for循环,然后介绍如何使用for/yield循环以及for循环内嵌if语句的办法(守卫语句)。鉴于match表达式和模式匹配对于Scala的重要性,在此将展示用以上方法解决一系列问题的技巧。

第5章 提供Scala类、参数和字段相关的例子。由于Scala的构造函数与Java的构造函数有很大的不同,因此我将用几个例子来展示实现主构造函数和辅助构造函数的来龙去脉。该章还会用几个例子介绍样例(case)类以及如何使用它们。

第6章 提供关于所有重要的Scala特质以及全新的枚举(enum)类型的例子。首先展示如何像Java接口一样使用特质;然后深入更高级的主题,比如如何用混入的方式使用特质,以及使用各种方法限制特质可以混合到哪些成员中;最后使用两个例子演示如何在领域建模中使用枚举,包括创建代数数据类型(Algebraic Data Type,ADT)。

第7章 提供有关对象的例子,包括对象作为一个类的实例的含义,以及与object关键字有关的内容。

第8章 介绍如何定义方法来接受参数、返回值,用指定参数名的方式调用方法,为方法参数设置默认值,创建可变参数,以及编写支持链式编程风格的方法。该章最后一个例子演示全新的Scala 3扩展方法。

第9章 提供Scala的package和import语句的例子。具体包括如何使用大括号去引入包、在导入依赖时如何隐藏和重命名成员等。

第10章 尽管本书的大部分内容都展示了函数式编程技术,但该章将重点介绍函数式编程的技巧,包括如何定义匿名函数(函数字面量)以及在各种场合下使用它们的方案。其中的例子包括如何定义一个接受函数参数的方法、部分应用函数,以及如何从函数中返回一个函数。

Scala集合库的内容丰富且深入,所以第11~15章提供了数百个与集合相关的例子。

第11章 介绍如何在不同的需求下选择合适的集合类以及方法来解决具体的问题,如不同集合间的转换、集合的过滤和创建集合的子集。

第12章 展示常见的集合类,包括Vector、List、ArrayBuffer、Array和LazyList。同时展示如何创建每种类型的集合,以及如何添加、更新和删除集合中的元素。

第13章 展示如何使用Scala序列类中最常见的方法,以及如何对序列进行迭代、转换、过滤和排序等。

第14章 展示许多用于Scala的Map类的技术(与第13章类似)。

第15章 提供其他Scala集合类的内容,包括元组(tuple)、范围(range)、集(set)、栈(stack)和队列(queue)。

第16章 展示如何处理文件和进程。首先介绍如何用Scala读写文件、获得目录列表和使用序列化,然后用几个例子来展示如何以独立于平台的方式与外部进程一起工作。

第17章 sbt是公认的Scala应用构建工具,该章是sbt的全面指南。首先展示创建sbt项目目录结构的几种方式,然后介绍如何引入托管依赖和非托管依赖、构建项目、为项目生成Scala文档(Scaladoc)以及部署项目等。

第18章 为利用Future和Akka actor库构建并发应用程序(以及提高多核CPU的利用率)提供解决方案。Future的例子将展示如何构建一次性的、短暂的并发运算集,actor的例子将展示如何创建长期存在的并行进程,这些进程在其生命周期内可能会响应数十亿的请求。

第19章 介绍如何在Web服务的客户端和服务器端同时使用Scala。在服务器端,将展示如何使用Play框架开发RESTful Web服务。客户端和服务器端示例代码将展示如何序列化和反序列化JSON,以及如何使用HTTP报头。

第20章 介绍Apache Spark框架。Spark是使Scala出名的应用之一,该章将展示如何将大型数据集作为弹性分布式数据集(Resilient Distributed Dataset,RDD)来处理,以及如何使用行业标准SQL查询。

第21章 提供几个Scala和JVM领域的库和工具的例子。前面几个例子展示如何使用Scala.js作为类型安全的JavaScript替代品。最后的例子展示如何使用GraalVM将Scala代码转换为原生的可执行文件,然后使用Java的jpackage工具将Scala应用打包成一个原生应用。

第22章 展示如何解决Scala与Java代码交互时可能遇到的问题。虽然Scala代码在与Java交互时通常能正常运行,但也有一些罕见的“陷阱”。该章将介绍如何解决Scala和Java由集合库的差异引发的问题,以及在Java代码中调用Scala代码时可能遇到的问题。

第23章 提供使用Scala强大的类型系统的技巧。从类型(type)开始,展示型变(variance)、边界(bound)和约束(constraint)等概念的例子,还将展示如何在类和方法定义中声明泛型、实现duck类型以及控制特质可以混入哪些类型。该章还会介绍几个全新的Scala 3概念,包括不透明类型(opaque type),使用given和using来替代Scala 2中的implicit类型、并集类型和交集类型,以及与比较对象时的相等性有关的两个例子。

第24章 由于这是一本介绍解决方案的书,我认为用一章专门介绍最佳实践(即如何以“Scala的方式”编写代码)是非常重要的。该章将展示如何创建没有副作用的函数,如何使用不可变的对象和集合类型,如何用表达式(而不是语句)来思考,如何使用模式匹配,以及如何消除代码中的null值。

安装Scala

你可以通过几种不同的方式安装Scala 3,包括Homebrew(在macOS上)、Coursier、SDKMAN,以及下载并手动安装Scala。Coursier被认为是“Scala安装程序”,它的使用方法在“Getting Started with Scala 3”的页面(https://oreil.ly/Czdpa)中有介绍。

如果你还不想安装Scala,也可以使用以下在线工具在浏览器中进行试验:

·Scastie(https://scastie.scala-lang.org

·ScalaFiddle(https://scalafiddle.io

代码风格约定

关于我在本书中使用的代码风格,有几个要点需要了解。首先,如前所述,我使用了新的可选大括号(显著缩进)编程风格,这减少了大部分的小括号和大括号的使用:

除了这种风格外,我还用4个空格缩进代码。目前还没有缩进的标准,开发人员似乎更喜欢用2~4个空格。

其次,我经常在代码后面的注释中展示相应的结果,看起来像这样:

使用这种风格有助于我在本书中包含更多第1版中没有的例子。

本书中使用的其他代码风格约定有:

·我总是把变量定义为val字段(就像Java中的final一样),除非有理由让它们成为var。

·当一个方法不需要参数并且有副作用(比如输出到控制台)时,我会使用def method(),而不是def method。

·虽然在许多情况下没有必要定义数据类型,但我总是声明公共方法的返回类型。如最后一个约定所述,你可以定义一个方法而不声明其返回类型,就像这样:

然而,大多数开发者更喜欢声明方法的返回类型:

现在只需多打几个字符,就能使你的代码在后期更容易阅读。

资源支持

本书中展示的许多示例源代码都可以在GitHub仓库github.com/bitlap/ScalaCookbook2-Examples中找到,其中包括许多完整的sbt项目。

Scala Gitter频道(https://gitter.im/lampepfl/dotty)是一个很好的帮助来源。你偶尔会在那里看到我的问题。

如果你对Scala功能的提案(SIP)和讨论感兴趣,“Scala Lontributors”论坛(https://contributors.scala-lang.org)也是一个非常好的资源。

最后,你可以在alvinalexander.com上找到我最新的博客文章,我经常在twitter.com/alvinalexander上发布关于Scala的话题。

排版约定

本书中使用以下排版约定:

斜体(Italic

表示新的术语、URL、电子邮件地址、文件名和文件扩展名。

等宽字体(Constant width)

用于程序清单,以及段落中的程序元素,例如变量名、函数名、数据库、数据类型、环境变量、语句以及关键字。

等宽斜体(Constant width italic

表示应由用户提供的值或由上下文确定的值替换的文本。

该图示表示提示或建议。

该图示表示一般性说明。

该图示表示警告或注意。

示例代码

可以从https://github.com/alvinj/ScalaCookbook2Examples下载补充材料(示例代码、练习、勘误等)。

这里的代码是为了帮助你更好地理解本书的内容。通常,可以在程序或文档中使用本书中的代码,而不需要联系O'Reilly获得许可,除非需要大段地复制代码。例如,使用本书中所提供的几个代码片段来编写一个程序不需要得到我们的许可,但销售或发布本书的示例代码则需要获得许可。引用本书的示例代码来回答问题也不需要许可,将本书中的很大一部分示例代码放到自己的产品文档中则需要获得许可。

非常欢迎读者使用本书中的代码,希望(但不强制)注明出处。注明出处时包含书名、作者、出版社和ISBN,例如:

Scala CookbookSecond Edition,作者Alvin Alexander,由O'Reilly出版,书号978-1-492-05154-1。

如果读者觉得对示例代码的使用超出了上面所给出的许可范围,欢迎通过permissions@oreilly.com联系我们。

O'Reilly在线学习平台(O'Reilly Online Learning)

40多年来,O'Reilly Media致力于提供技术和商业培训、知识和卓越见解,来帮助众多公司取得成功。

我们拥有独一无二的专家和革新者组成的庞大网络,他们通过图书、文章、会议和我们的在线学习平台分享他们的知识和经验。O'Reilly的在线学习平台允许你按需访问现场培训课程、深入的学习路径、交互式编程环境,以及O'Reilly和200多家其他出版商提供的大量文本和视频资源。有关的更多信息,请访问http://oreilly.com

如何联系我们

对于本书,如果有任何意见或疑问,请按照以下地址联系本书出版商。

美国:

O'Reilly Media,Inc.

1005 Gravenstein Highway North

Sebastopol,CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

要询问技术问题或对本书提出建议,请发送电子邮件至errata@oreilly.com.cn

本书配套网站https://oreil.ly/scala-cookbook-2e上列出了勘误表、示例以及其他信息。

关于书籍、课程、会议和新闻的更多信息,请访问我们的网站https://oreilly.com

我们在Facebook上的地址:http://facebook.com/oreilly

我们在Twitter上的地址:http://twitter.com/oreillymedia

我们在YouTube上的地址:http://www.youtube.com/oreillymedia

致谢

写这么厚的一本书需要耗费很多时间,我要感谢责任编辑Jeff Bleiel一直在写作的过程中鼓励我,在遇到困难时让我保持头脑清醒。我们从2018年12月开始合作,虽然Scala 3在社区中不断变化,但我们一直在一起工作,直到2021年该书完成。

当我完成各章的初稿时,Jeff就如何改进它们提出了数百条建议。随着书中的内容越来越清晰,Jeff(正确地)提议重新组织部分章节的内容。他对工作非常负责,当你看到一本由Jeff Bleiel担任责编的书时,你可以确信它是经过精心编辑和思考的。

所有的审校者都以不同的方式为本书提供了帮助。Jason Swartz(https://twitter.com/swartzrock)是本书第1版的“最有价值审校者”候选人,他在这一版中又做了出色的工作,提出了许多可靠的建议。

Philip Schwarz(https://twitter.com/philip_schwarz)对本书提供了许多很好的见解,特别是本书的早期版本。

我要特别感谢Hermann Hueck(https://twitter.com/hermannhueck),他是本书的“最有价值审校者”。Hermann提供了数百条建议,涵盖了从一行代码到本书的整体组织的各个方面。

我对Jeff和Hermann的评价是不言而喻的。但也许最好的说法是,没有他们,本书就不会像现在这样完善。

我还要感谢本书的制作编辑Christopher Faucher。在本书定稿后,他帮助我们完成了出版的一系列流程,这期间我们处理了数百条意见和问题。他让我知道,让一本书出版和让一个大型软件面世一样富有挑战。

最后,我要感谢Martin Odersky(https://twitter.com/odersky)和他的团队,感谢他们创造了如此有趣的编程语言。2010年,我在阿拉斯加州安克雷奇的一家书店发现了他的Programming in Scala一书,从此便爱上了Scala。


[1]implicit(隐式转换)是Scala的一种预编译特性,编译器会根据implicit相关的规划,在程序中自动插入代码,以修正类型错误。