В чём разница между 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 предпочтителен и отражает философию языка, ориентированную на безопасные, чистые и предсказуемые вычисления.