Изменяемые по сравнению с неизменяемыми в Scala

Есть ли в Scala руководящие указания относительно того, когда использовать val с изменяемой коллекцией по сравнению с использованием var с неизменяемой коллекцией? Или вы действительно должны стремиться к Вэл с неизменной коллекцией?

Тот факт, что есть оба типа коллекций, дает мне большой выбор, и часто я не знаю, как сделать этот выбор.

5 ответов

Решение

Это довольно распространенный вопрос. Трудно найти дубликаты.

Вы должны стремиться к ссылочной прозрачности. Это означает, что, если у меня есть выражение "е", я мог бы сделать val x = eи заменить e с x, Это свойство, которое нарушает изменчивость. Всякий раз, когда вам нужно принять проектное решение, максимально увеличить ссылочную прозрачность.

На практике метод местный var самый безопасный var что существует, так как он не избегает метода. Если метод короткий, даже лучше. Если это не так, попробуйте уменьшить его, извлекая другие методы.

С другой стороны, изменчивая коллекция может скрыться, даже если это не так. При изменении кода вы можете захотеть передать его другим методам или вернуть. Это та вещь, которая нарушает ссылочную прозрачность.

На объекте (поле) происходит почти то же самое, но с более тяжелыми последствиями. В любом случае объект будет иметь состояние и, следовательно, нарушать ссылочную прозрачность. Но наличие изменяемой коллекции означает, что даже сам объект может потерять контроль над тем, кто его меняет.

Если вы работаете с неизменяемыми коллекциями и вам необходимо "изменить" их, например, добавить в них элементы в цикле, то вам придется использовать varпотому что вам нужно где-то хранить полученную коллекцию. Если вы читаете только из неизменных коллекций, используйте vals.

В общем, убедитесь, что вы не путаете ссылки и объекты. vals являются неизменяемыми ссылками (постоянные указатели в C). То есть, когда вы используете val x = new MutableFoo(), вы сможете изменить объект, который x указывает на, но вы не сможете изменить какой объект x точки. Противоположность имеет место, если вы используете var x = new ImmutableFoo(), Подбирая мой первоначальный совет: если вам не нужно менять, к какому объекту относятся контрольные точки, используйте vals.

Лучший способ ответить на этот вопрос - на примере. Предположим, у нас есть какой-то процесс, просто собирающий числа по какой-то причине. Мы хотим записать эти номера и отправим коллекцию другому процессу для этого.

Конечно, мы все еще собираем цифры после того, как отправим коллекцию в регистратор. И скажем, есть некоторые издержки в процессе регистрации, которые задерживают фактическую регистрацию. Надеюсь, вы увидите, куда это идет.

Если мы храним эту коллекцию в изменчивом val(изменяемый, потому что мы постоянно добавляем к нему), это означает, что процесс, выполняющий ведение журнала, будет смотреть на тот же объект, который все еще обновляется нашим процессом сбора. Эта коллекция может быть обновлена ​​в любое время, поэтому, когда пришло время регистрироваться, мы, возможно, не регистрируем отправленную коллекцию.

Если мы используем неизменный varмы отправляем неизменную структуру данных в регистратор. Когда мы добавим больше номеров в нашу коллекцию, мы заменим наши var с новой неизменной структурой данных. Это не означает, что коллекция, отправленная регистратору, заменена! Он все еще ссылается на коллекцию, которую он отправил. Таким образом, наш регистратор действительно зарегистрирует полученную коллекцию.

Я думаю, что примеры в этом сообщении блога будут проливать больше света, так как вопрос о том, какую комбинацию использовать, становится еще более важным в сценариях параллелизма: важность неизменности для параллелизма. И пока мы это делаем, обратите внимание на предпочтительное использование синхронизированного и @volatile против чего-то вроде AtomicReference: три инструмента

var immutable против val mutable

Помимо множества отличных ответов на этот вопрос. Вот простой пример, который иллюстрирует потенциальную опасность val mutable:

Изменяемые объекты могут быть изменены внутри методов, которые принимают их в качестве параметров, в то время как переназначение запрещено.

import scala.collection.mutable.ArrayBuffer

object MyObject {
    def main(args: Array[String]) {

        val a = ArrayBuffer(1,2,3,4)
        silly(a)
        println(a) // a has been modified here
    }

    def silly(a: ArrayBuffer[Int]): Unit = {
        a += 10
        println(s"length: ${a.length}")
    }
}

Результат:

length: 5
ArrayBuffer(1, 2, 3, 4, 10)

Нечто подобное не может случиться с var immutableпотому что переназначение не допускается:

object MyObject {
    def main(args: Array[String]) {
        var v = Vector(1,2,3,4)
        silly(v)
        println(v)
    }

    def silly(v: Vector[Int]): Unit = {
        v = v :+ 10 // This line is not valid
        println(s"length of v: ${v.length}")
    }
}

Результаты в:

error: reassignment to val

Поскольку параметры функции рассматриваются как val это переназначение не допускается.

Другие вопросы по тегам