Scala 常见问题解答

语言

常见问题解答,附有简短答案和/或指向更长答案的链接。

此列表仅包含在 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/scala-lang#1040

技术问题

可用选项列表 在此处

人们选择的标志因商店而异,因人而异。启用 -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 库,仅进行一些小的补充。这就是 versionStringversionNumberString 报告正在使用 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 如何工作?

它是嵌套 mapflatMapwithFilter 调用的语法糖。

有关深入解释,请参阅 此 Stack Overflow 答案

视图、流和迭代器之间有什么区别?

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 上的此答案

后者具有单例类型,过于具体。请参阅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

以下是一些进一步的阅读材料

此页面的贡献者