val и var: неизменяемость по умолчанию
В Scala выбор между val и var — это маленькое решение с большими последствиями для надёжности кода.
«Неизменяемость — это не ограничение, а суперсила: то, что не может измениться, не может сломаться неожиданно.»
В большинстве языков переменная — это коробка, в которую можно класть разные значения. В Scala есть два вида «коробок». val — это коробка, которую запаяли: положили значение один раз и больше не меняем. var — обычная переменная, содержимое можно перезаписывать.
val pi = 3.14 // нельзя переприсвоить
var counter = 0 // можно менять
counter = counter + 1 // ок
// pi = 3.15 // ОШИБКА компиляции!Почему это важно
Когда вы видите val, вы точно знаете: это значение никогда не изменится. Не нужно держать в голове, кто и где мог его перезаписать. В больших программах это огромная экономия внимания. Поэтому правило Scala: по умолчанию используй val, а var — только когда без изменения действительно не обойтись.
Типы можно не писать
Scala умеет выводить тип сама (type inference). Но при желании тип указывают явно после двоеточия:
val age: Int = 25
val price: Double = 9.99
val title: String = "Scala"
val ready: Boolean = trueТа же идея на Python ▶
# В Python нет val/var, но идею неизменяемости передаёт кортеж
# и соглашение об именах (КОНСТАНТЫ в верхнем регистре)
PI = 3.14 # по соглашению не меняем (как val)
counter = 0 # обычная переменная (как var)
counter = counter + 1
print(PI, counter)
# Настоящая неизменяемость — кортеж
point = (10, 20) # элементы менять нельзяНеизменяемые ссылки и неизменяемые данные
Важный нюанс: val запрещает переприсваивать ссылку, но если внутри сидит изменяемый объект, его содержимое всё ещё можно поменять. Поэтому в Scala предпочитают не только val, но и неизменяемые коллекции — о них позже.
val x = коробка -> [значение 5] переприсвоить x -> ЗАПРЕЩЕНО var y = коробка -> [значение 5] y = 7 -> коробка -> [значение 7] ОК
Как работает под капотом (JVM)
На уровне JVM val часто компилируется в final-поле или локальную переменную, которую нельзя переприсвоить. JVM любит final: такие значения проще оптимизировать и безопаснее использовать в многопоточном коде, потому что они гарантированно не «прыгают» под ногами у других потоков. var же становится обычным изменяемым полем.
Частые ошибки
- Лепить
varвезде по привычке из других языков. В Scala это сигнал «здесь что-то меняется» — и часто его можно избежать. - Думать, что
valс изменяемым списком внутри полностью неизменяем. Ссылка зафиксирована, а содержимое — нет. - Бояться писать типы. Иногда явный тип делает код понятнее, особенно в сигнатурах функций.
Best practices
- Начинайте всегда с
val. Меняйте наvarтолько если компилятор или логика вынуждают. - Указывайте типы для публичных значений и параметров функций — это документация.
- Сочетайте
valс неизменяемыми коллекциями для полной безопасности.
Неизменяемость как стиль мышления
Привычка предпочитать val постепенно меняет то, как вы проектируете программы. Вместо того чтобы заводить переменную и многократно её перезаписывать по ходу вычислений, вы начинаете описывать данные как цепочку преобразований: из одного неизменяемого значения получается следующее. Такой код легче читать, потому что каждое имя означает ровно одну вещь и никогда не меняет смысла на протяжении своей жизни.
Особенно ярко выгода проявляется в многопоточных программах. Если данные не меняются, их можно безопасно читать из множества потоков одновременно — никаких блокировок, никаких гонок. Именно поэтому функциональный стиль так хорошо ложится на современные многоядерные системы. Начиная с привычки к val, вы закладываете фундамент для масштабируемого и безопасного кода, даже если пока пишете простые однопоточные программы.
Хороший практический ориентир: если в коде появляется var, остановитесь и спросите, нельзя ли выразить ту же логику через преобразование неизменяемых значений. Часто ответ — да, и результат оказывается чище. Изменяемое состояние не запрещено, но в Scala оно должно быть осознанным выбором, а не значением по умолчанию из привычки.
Итоги. val — неизменяемое значение (предпочтительно), var — изменяемая переменная (по нужде). Неизменяемость делает код предсказуемым. Дальше — система типов Scala целиком.