Здесь есть пять разных вопросов, давайте разберемся с ними один за другим.
Почему 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._
Последний является импортом по шаблону (который импортирует изменяемую карту по требованию), тогда как первый импортирует только символ mutable
.Поэтому в вашем коде доступен только символ 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._
?
В спецификации указывается приоритет импорта :
- Определения и объявления, которые являются локальными, наследуются или становятся доступными с помощью предложения пакета в той же единице компиляциигде определение имеет наивысший приоритет.
- Явные импорты имеют следующий наивысший приоритет.
- Импорт подстановочных знаков имеет следующий наивысший приоритет.
- Определения, доступные в предложении пакета, которого нетблок компиляции, в котором происходит определение, имеет наименьший приоритет.
Следовательно, из-за пунктов 2 и 3 явный импорт
import scala.collection.mutable.Map
имеет приоритет над подстановочным символомd import
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.Однако здесь здесь написано:
Каждая единица компиляции неявно импортирует следующие пакеты в указанном порядке:
- пакет java.lang,
- пакет scala и
- объект scala.Predef, если нет явного импорта верхнего уровня, который ссылается на scala.Predef.
Членыпоследующий импорт в таком порядке скрывает членов более раннего импорта.
Я думаю, что последнее предложение также охватывает импорт, который идет после 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()
}
}
, поэтому последний импорт имеет приоритет.Я не уверен, как Скотти справится с этим, возможно, там то же самое, так что это еще одно осложнение, которое вы должны иметь в виду.