Scala:使用 immutable.Map 不需要导入?

Scala: import not required to use immutable.Map?

我在这里学习 Scala https://scastie.scala-lang.org/iRJ8VOw7TySZ4KQ5qlWkdw

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.SortedSet
// import scala.collection.immutable.Map // import not required to use immutable.Map

val m = Map((1 -> "a"), (2 -> "bb"))

val r = m.filter(_._1 > 1)

r // Map(2 -> bb): scala.collection.immutable.Map

println(r)

在上面的代码中,r 是类型 scala.collection.immutable.Map,即使 scala.collection.immutable.Map 未导入但 scala.collection.mutable 已导入。

如果导入 scala.collection.immutable.Mapscala.collection.mutable._r 就是 immutable.Map。如果 import scala.collection.mutable._rmutable.Map

我有点困惑。谁能帮忙解释一下?谢谢

默认情况下,Scala 中的 Map 是不可变的。 您不导入 mutable.Map,因此它仍然是不可变的。 如果你像这样导入它应该可以工作:

import scala.collection.mutable.Map

if import scala.collection.immutable.Map and scala.collection.mutable., r is immutable.Map. if import scala.collection.mutable., r is mutable.Map.

最符合条件的导入语句获胜。

tl;dr 使用 import scala.collection.mutable.Map


这是因为 mscala.collection.immutable.Map 的一个实例(默认行为)。 java 中的类似行为 java.lang 包中的所有内容都被隐式导入。

你导入了不可变的包,这不会自动进入作用域scala.collection.immutable.Map隐藏scala.collection.mutable.Map
为此你应该import scala.collection.mutable.Map

import scala.collection.mutable
val m = Map((1 -> "a"), (2 -> "bb"))
println(m.getClass)  // class scala.collection.immutable.Map$Map2

val mutableMap = mutable.Map((1->"A"), (2->"BB"))
println(mutableMap.getClass) // class scala.collection.mutable.HashMap

或者

import scala.collection.mutable.Map
val m = Map((1 -> "a"), (2 -> "bb"))
println(m.getClass)  // class scala.collection.mutable.HashMap

第一个问题是 Map 不是从 scale.collection.mutable 导入的,因为你的 import 语句有问题。

改变

import scala.collection.mutable

import scala.collection.mutable._

import scala.collection.mutable.Map

Map 将是 mutable.Map

当您同时导入 scale.collection.immutable.Mapscala.collection.mutable._ 时,immutable Map 获胜,因为它更具体。因此,最具体的 import 获胜,在平局的情况下,您将收到编译器错误。

通过默认 Scala 为我们提供了不可变映射。有两种方法创建可变映射:

**1.** Use import statement - *scala.collection.mutable._* to bring mutable map it into scope 
**2.** Specify the full path to *scala.collection.mutable.Map*

通过导入“导入scala.collection.mutable._”,您将导入所有可用的可变集合以及可变映射 .

如果您将 scala.collection.immutable.Mapscala.collection.mutable._ 一起添加,则 Scala 编译器使用所有可变集合,但对于 Map,使用不可变集合。

这里有五个不同的问题,让我们一一解决。


为什么import collection.mutable貌似什么都没做?

如果我们从您的代码片段中省略不必要的细节,我们将获得:

import scala.collection.mutable
val r = Map(2 -> 3)

你问为什么 r 仍然是 scala.collection.immutable.Map

首先,请注意

import scala.collection.mutable

不一样
import scala.collection.mutable._

后者是通配符导入(按需导入可变Map),而前者只导入符号[=​​25=]。因此,在您的代码中,只有符号 mutable 可用,但这不会以任何方式影响 Map 的含义。


即使什么都不导入,scala.collection.immutable.Map从哪里来?

Predef 始终是隐式导入的(除非您使用编译器标志将其停用)。有类型别名定义

type Map[A, +B] = collection.immutable.Map[A, B] 

Predef,还有immutable.Map的伴生对象的直接快捷方式:

val Map: collection.immutable.Map.type 

允许您使用 Map(...)-语法构建地图,无需 new-关键字。


为什么import collection.immutable.Map优先于import collection.mutable._

specification states about the precedence of imports:

  1. Definitions and declarations that are local, inherited, or made available by a package clause in the same compilation unit where the definition occurs have highest precedence.
  2. Explicit imports have next highest precedence.
  3. Wildcard imports have next highest precedence.
  4. Definitions made available by a package clause not in the compilation unit where the definition occurs have lowest precedence.

因此,由于第 2 点和第 3 点,显式导入

import scala.collection.mutable.Map

优先于通配符导入

import scala.collection.immutable._

为什么 import collection.mutable._ 使 Map 可变?

现在,最后一件事需要澄清:为什么 mutable._ 通配符导入优先于 Predef 中的定义?这是一个演示它的简短示例:

import scala.collection.mutable._
val m = Map()
println(m.getClass)

输出是(可能有点令人惊讶):

class scala.collection.mutable.HashMap

规范在这一点上似乎有点含糊,至少我找不到它在哪里说普通通配符导入掩盖了 Predef 中的定义。但是,here 它说:

Every compilation unit implicitly imports the following packages, in the given order:

  • the package java.lang,
  • the package scala, and
  • the object scala.Predef, unless there is an explicit top-level import that references scala.Predef.

Members of a later import in that order hide members of an earlier import.

我认为最后一句话也涵盖了 scala.Predef 之后的导入,即显式通配符导入。这可能就是为什么 import scala.collection.mutable._ 掩盖了 Predef.

中的定义

REPL 中发生了什么?

当您尝试导入时,您必须牢记 REPL 评估每一行,就好像它在一个新的嵌套块中一样。这意味着,例如,脚本

import scala.collection.mutable._
import scala.collection.immutable._
Map()

将导致

error: reference to Map is ambiguous

但是如果你在 REPL 中一行一行地输入相同的行,那么就不会发生错误,你会得到一个不可变的 Map。这是因为在 REPL 中,上面的行被解释为

import scala.collection.mutable._
{
  import scala.collection.immutable._
  {
    Map()
  }
}

以便最后一次导入优先。我不确定 Scastie 是如何处理它的,可能它在那里是一样的,所以这是你必须记住的一个额外的并发症。