常见问题解答,附有简短答案和/或指向更长答案的链接。
此列表仅包含在 Scala 聊天室和论坛中实际反复出现的问题。
一般问题
我可以在哪里询问 Scala 问题?
请参阅我们的 社区页面。
有什么关于 Scala 的好书?
我们的 书籍页面 列出了一些特别受欢迎、广为人知的书籍。
我们没有列出所有已出版的 Scala 书籍;有很多。
你可以在 Discord 上的 #scala-users 房间或其他社区论坛上询问书籍推荐。如果你提供一些关于你的背景以及你想要学习 Scala 的原因的信息,你将获得更有帮助的答案。
我应该学习 Scala 2 还是 Scala 3?
不要太纠结于这个决定。无论哪种方式,你都不会走错。稍后切换并不难,无论哪个方向。
Scala 2 仍然是一个常见且合理的选择。大多数 Scala 工作仍然是 Scala 2 工作。Scala 2 还拥有一个更大的库生态系统、更好的工具支持以及更广泛的学习材料。
尽管如此,这些差距每个月都在缩小。有大量书籍可用,已经存在一个丰富的库生态系统,并且使用 Scala 3 的工作越来越多。你应该强烈考虑学习 Scala 3。它仍然相当新,于 2021 年发布。但它是未来,也是爱上这门语言及其所提供的一切的最佳版本。
Scala 工作在哪里发布广告?
这在我们的 社区页面 上有说明。
简而言之,唯一官方认可的地方是 Discord 上的 #jobs 频道。
Scala 背后是谁?
这个问题在 社区页面 中得到解答。
我可以使用 Scala 徽标吗?
技术问题
推荐使用哪些编译器标志?
可用选项列表 在此处。
人们选择的标志因商店而异,因人而异。启用 -Xlint
很有价值。一些勇敢的人启用 -Werror
(以前为 -Xfatal-warnings
)以使警告致命。
sbt-tpolecat 是一个固执己见的 sbt 插件,它根据 Scala 版本自动设置许多选项;您可以 在此处 查看它设置的内容。它做出的某些选择面向纯函数式程序员。
如何查找某些符号的含义或作用?
Stack Overflow 回答 阐述了 Scala 中不同种类的符号,并解释了最常用的符号。
Scala 允许使用符号方法名。因此,如果您在 Scala 代码中看到一个类似 >=@=>
的随机运算符,它可能只是某个库中的一个方法,而不是在语言本身中具有任何特殊含义。
您可以在 Google 上搜索符号。例如,如果您想知道 <:<
的含义,搜索 scala <:<
就可以了。如果您获得较差的结果,请尝试用双引号将符号括起来。
我想要 Scala 2.13(或其他版本);为什么 sbt 说它正在使用 Scala 2.12?
sbt 1.x 始终使用 Scala 2.12 来编译构建定义。您的 sbt 1.x 构建定义始终是一个 Scala 2.12 程序。
无论如何,在您的 build.sbt
中,您可以将 scalaVersion
设置为您想要的任何可用发行版,您的程序代码将使用该版本进行编译。
我想要 Scala 3。为什么 versionNumberString
说我正在使用 2.13?
为了帮助迁移,Scala 3 目前原样使用 Scala 2.13 库,仅进行一些小的补充。这就是 versionString
和 versionNumberString
报告正在使用 Scala 2 的原因
Welcome to Scala 3.3.2 (17.0.3, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> util.Properties.versionNumberString
val res0: String = 2.13.12
请注意,即使是最新版本的 Scala 3 也可能不会使用最新的 Scala 2 标准库,因为 3 和 2 的发布计划并不协调。
那么,如何询问 Scala 3 的版本号?Scala 3 提供 dotty.tools.dotc.config.Properties.versionNumberString
,但前提是 classpath 中有 scala3-compiler。因此,这在 Scala 3 REPL 中有效,但在典型的 Scala 3 应用程序代码中无效。
有关检测 Scala 3 版本的替代方法,请参阅 此 gist。
为什么我的(抽象或重写)val
为 null?
请参阅 此内容。
我应该选择哪种类型的集合?
请参阅 Scala 2.13 集合指南。
什么是上下文边界?
它是上下文参数的语法糖(Scala 2 中的 implicit
参数,或 Scala 3 中的 using
参数)。
有关更多详细信息,请参阅 Scala 3 书籍的此部分 和 此 Stack Overflow 答案。
for / yield
如何工作?
它是嵌套 map
、flatMap
和 withFilter
调用的语法糖。
有关深入解释,请参阅 此 Stack Overflow 答案。
视图、流和迭代器之间有什么区别?
_
是什么意思?
实际上有很多意思,具体取决于上下文。 Stack Overflow 上的此答案 很好的总结了它的所有含义。
请注意,即使具体含义不同,根据情况,它通常表示“任何东西”。
为什么我的带有 _
的函数字面量不起作用?
并非所有函数字面量(又名 lambda)都可以用 _
语法表示。
每次出现 _
都会引入一个新变量。因此,_ + _
表示 (x, y) => x + y
,而不是 x => x + x
。后者函数无法使用 _
语法编写。
此外,_
的作用域始终是最小的封闭表达式。作用域在解析期间纯粹通过语法确定,而不考虑类型。例如,foo(_ + 1)
始终表示 foo(x => x + 1)
;它永远不会表示 x => foo(x + 1)
。后者函数无法使用 _
语法编写。
另请参阅 SLS 6.23.2。
为什么 Scala 无法推断出我代码中的正确类型?
很难对类型推断进行概括,因为语言的各种特性会影响代码的解释方式。可能有几种方法可以重写代码,以使类型自然而然地消失。
最直接的解决方法是在代码中提供显式类型。
这可能涉及为定义指定显式类型,或为方法指定类型参数。
Scala 3 中的类型推断得到了极大的改进。如果 Scala 2 无法编译你的代码,那么值得尝试使用 Scala 3。
有时,使用多个参数列表有助于推断,如 语言指南的这一部分 所述。
有关涉及 toSet
的类型推断的常见问题,请参阅 此问题 和相关的 问答 中的讨论。
我可以链接或嵌套隐式转换吗?
实际上不行,但你可以 让它起作用。
但是,请注意,一般来说,不建议使用 隐式转换。
Scala 在哪里查找隐式内容?
请参阅 Stack Overflow 上的此答案。
为什么基本类型参数会擦除为 Object
?
例如,Scala 代码中的 List[Int]
在 Java 中将显示为 List[Object]
。Java 类型系统不允许基本类型显示为类型参数,但它们不能显示为其装箱等效项,例如 List[java.lang.Integer]
吗?
人们会希望如此,但尝试这样做,结果证明这是不可能的。 此 SO 问题 遗憾的是缺乏简洁的解释,但它确实链接到了过去的讨论。
方法和函数之间有什么区别?
例如,像这样的方法
def square(x: Int): Int = x * x
与像这样的函数值有什么不同
val square: Int => Int = x => x * x
对于 Scala 2,Stack Overflow 上有一个完整的答案和一个总结了实际差异的摘要。
请注意,在 Scala 3 中,差异较少;例如,它们将能够接受隐式参数以及类型参数。
不过,仍然建议在大多数情况下使用方法,除非你绝对需要一个函数。而且,感谢eta 扩展,你很少需要定义一个函数而不是一个方法。
类型和类之间有什么区别?
类型主要是一个编译时概念。在编译时,编译器会为每个表达式分配一个类型。
类主要是一个运行时概念,并且依赖于平台。在 JVM 上的运行时,每个值要么是基本值,要么是恰好一个类的实例。
某些类型信息仅在编译时存在,原因有很多,最臭名昭著的是类型擦除。
有关类型与类的深入探讨,请参阅博客文章“类型比类更多”。
超类中的方法如何返回“当前”类型的值?
首先,请注意使用 this.type
不会起作用。人们经常尝试这样做,但 this.type
意味着“此实例的单例类型”,这是一个不同且过于具体的含义。只有 this
本身具有类型 this.type
;其他实例没有。
什么有效?可能的解决方案包括 F 边界多态性(Java 程序员熟悉)、类型成员和类型类模式。
此博客文章反对 F 边界,支持类型类;另请参阅此 Stack Overflow 帖子了解一些反驳意见。
<:<
是什么意思?
它是一个“类型约束”,它来自标准库,而不是来自语言本身。请参阅此博客文章。
我不喜欢要求调用者将可选参数包装在 Some(...)
中;有更好的方法吗?
并非如此。请参阅Stack Overflow 上的此答案。
为什么通常建议使用 implicit val
而不是 implicit object
?
后者具有单例类型,过于具体。请参阅Stack Overflow 上的答案。
在编译代码时遇到了 StackOverflowError
。这是编译器错误吗?
可能是。
要找出原因,请尝试为编译器提供更多堆栈,看看错误是否消失。
在编译某些类型的深度嵌套代码时,编译器可能会耗尽堆栈。JVM 的默认堆栈大小相当小,因此这可能会比你预期的更快发生。
可以通过在 JVM 启动时传递 -Xss...
来更改堆栈大小,例如 -Xss16M
。具体操作方法取决于你使用的 IDE 和/或构建工具。对于 sbt,将其添加到 .jvmopts
中。
无论你为编译器提供多少堆栈,堆栈溢出都不会消失,那么这就是编译器错误。请在Scala 2 错误跟踪器或Scala 3 错误跟踪器上报告此问题,但首先检查它是否与现有工单重复。
我在 sbt 中设置了一个设置,但没有任何反应。为什么?
可能有很多原因。一个非常常见的原因是,几乎每个人迟早都会遇到,即在多项目构建中有一个裸设置。
例如,如果你将以下内容添加到 build.sbt
中
scalaVersion := "2.13.13"
这是一个“裸”设置,你可能希望它适用于整个构建。但事实并非如此。它只适用于根项目。
在许多情况下,应该改为编写
ThisBuild / scalaVersion := "2.13.13"
其他可能性包括
- 常见的设置模式,其中你将共享设置放在
val
中,通常命名为commonSettings
,然后在要应用它们的每个项目中.settings(commonSettings)
。 - 仅在交互式使用中,
set every
以下是一些进一步的阅读材料