В чём разница между val и var?

В языке программирования Scala ключевые слова val и var используются для объявления переменных, но они имеют принципиальные различия в семантике, связанной с изменяемостью значений. Понимание этой разницы важно для написания безопасного, функционального и выразительного кода.

val: неизменяемое значение (immutable)

Когда переменная объявляется с использованием val, это означает, что ссылка на значение не может быть переназначена. То есть вы не можете переприсвоить val на новое значение после инициализации.

Синтаксис:

val name = "Alice"

Особенности:

  • val аналогичен final в Java.

  • После инициализации нельзя сделать name = "Bob" — компилятор выдаст ошибку.

  • Это не означает, что объект по ссылке неизменяем. Только сама ссылка неизменяема.

Пример:

val list = scala.collection.mutable.ListBuffer(1, 2, 3)
list += 4 // разрешено: объект изменяем
// list = ListBuffer(5, 6) // Ошибка: нельзя переназначить ссылку

Применение:

Используется по умолчанию, когда нет явной необходимости в изменении значения. Такой подход соответствует функциональному стилю Scala.

var: изменяемое значение (mutable)

var означает, что переменная может быть переназначена на другое значение после объявления.

Синтаксис:

var age = 30
age = 31 // допустимо

Особенности:

  • Аналог обычных переменных в Java, Python.

  • Разрешено присваивать новое значение переменной.

  • Может использоваться, если требуется изменять состояние, например, счётчик в цикле.

Пример:

var counter = 0
counter += 1
counter = counter \* 2

Когда использовать:

Только в случаях, когда действительно нужно изменяемое состояние, особенно в императивном стиле программирования. Например, в тестах, симуляциях, мьютексах, внутренних буферах.

val и изменяемость внутреннего состояния

Важно понимать, что val не делает объект неизменяемым, если он сам по себе изменяемый. Он просто запрещает переприсваивать ссылку. Пример:

val buffer = scala.collection.mutable.ArrayBuffer(1, 2, 3)
buffer += 4 // допустимо
buffer(0) = 42 // допустимо
// buffer = ArrayBuffer(10, 11) // Ошибка!

В этом примере buffer неизменяем по ссылке, но сам ArrayBuffer допускает мутации.

Сравнение по таблице:

Критерий val var
Изменяемость ссылки Нельзя Можно
--- --- ---
Изменяемость объекта Зависит от типа (mutable/immutable) Зависит от типа
--- --- ---
Стиль Функциональный (preferred) Императивный
--- --- ---
Безопасность Выше Ниже
--- --- ---
Потокобезопасность Проще обеспечить Требует дополнительной синхронизации
--- --- ---

Как выбрать между val и var

  • По умолчанию: всегда начинай с val. Это безопаснее, проще тестировать, легче читать.

  • Если нужно изменять: тогда — var.

  • С коллекциями: использовать val для неизменяемых коллекций (например, List, Vector) или для ссылок на изменяемые коллекции, если ссылка не должна меняться.

Особое поведение с типами:

val x: Int = 5
var y: Int = 10

Scala использует типовую систему с выводом типов (type inference). Можно писать val x = 5, и компилятор выведет тип Int автоматически.

Связь с функциональным стилем

Scala поддерживает функциональное программирование. В нём основной акцент делается на неизменяемость. Использование val помогает избежать неожиданных сайд-эффектов и упрощает параллельное выполнение. С другой стороны, var делает код ближе к традиционным языкам вроде Java, но также делает его более сложным для отладки и сопровождения.

Примеры на контрасте

// Функциональный стиль  предпочтительный
def increment(x: Int): Int = x + 1
// Императивный стиль  с var
def incrementVar(): Int = {
var x = 0
x += 1
x
}

Первый вариант с val и параметрами безопасен, повторяем, тестируем. Второй требует больше контроля.

Таким образом, val — это неизменяемая ссылка на объект, а var — изменяемая. Разница между ними заключается в возможности переприсваивать переменные. Хотя оба можно использовать в Scala, val предпочтителен и отражает философию языка, ориентированную на безопасные, чистые и предсказуемые вычисления.