В чем разница между определениями var и val в Scala?
В чем разница между var
а также val
определение в Scala и зачем языку нужны оба? Почему бы вы выбрали val
через var
и наоборот?
14 ответов
Как говорили многие другие, объект, назначенный val
не может быть заменен, и объект, назначенный var
Можно. Однако у указанного объекта может быть изменено его внутреннее состояние. Например:
class A(n: Int) {
var value = n
}
class B(n: Int) {
val value = new A(n)
}
object Test {
def main(args: Array[String]) {
val x = new B(5)
x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
x.value.value = 6 // Works, because A.value can receive a new object.
}
}
Таким образом, хотя мы не можем изменить объект, назначенный x
мы могли бы изменить состояние этого объекта. В корне этого, однако, был var
,
Сейчас неизменность - это хорошая вещь по многим причинам. Во-первых, если объект не меняет внутреннее состояние, вам не нужно беспокоиться, если какая-то другая часть вашего кода изменит его. Например:
x = new B(0)
f(x)
if (x.value.value == 0)
println("f didn't do anything to x")
else
println("f did something to x")
Это становится особенно важным с многопоточными системами. В многопоточной системе может произойти следующее:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Если вы используете val
исключительно, и использовать только неизменные структуры данных (то есть избегать массивов, все в scala.collection.mutable
и т.д.), вы можете быть уверены, что этого не произойдет. То есть, если какой-то код, возможно, даже фреймворк, не выполняет трюки отражения - отражение, к сожалению, может изменить "неизменяемые" значения.
Это одна из причин, но есть и другая причина. Когда вы используете var
, вы можете соблазниться к повторному var
для нескольких целей. Это имеет некоторые проблемы:
- Людям, читающим код, будет сложнее узнать, каково значение переменной в определенной части кода.
- Вы можете забыть повторно инициализировать переменную в некотором пути кода и в конечном итоге передать неверные значения в коде.
Проще говоря, используя val
безопаснее и приводит к более читаемому коду.
Тогда мы можем пойти в другом направлении. Если val
это лучше, почему есть var
совсем? Ну, некоторые языки пошли по этому пути, но бывают ситуации, когда изменчивость значительно повышает производительность.
Например, взять неизменный Queue
, Когда вы либо enqueue
или же dequeue
вещи в нем, вы получите новый Queue
объект. Как тогда, вы бы пошли на обработку всех предметов в нем?
Я пройдусь по этому примеру. Допустим, у вас есть очередь цифр, и вы хотите составить из них число. Например, если у меня есть очередь с 2, 1, 3, в этом порядке, я хочу вернуть число 213. Давайте сначала решим это с помощью mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Этот код быстр и прост для понимания. Его главный недостаток в том, что переданная очередь модифицируется toNum
, поэтому вы должны сделать копию этого заранее. Это тот тип управления объектами, который делает вас неизменным.
Теперь давайте перейдем к immutable.Queue
:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
if (qr.isEmpty)
num
else {
val (digit, newQ) = qr.dequeue
recurse(newQ, num * 10 + digit)
}
}
recurse(q, 0)
}
Потому что я не могу использовать какую-то переменную, чтобы отслеживать мои num
Как и в предыдущем примере, мне нужно прибегнуть к рекурсии. В данном случае это хвостовая рекурсия, которая имеет довольно хорошую производительность. Но это не всегда так: иногда просто не существует хорошего (удобочитаемого, простого) решения для хвостовой рекурсии.
Обратите внимание, однако, что я могу переписать этот код, чтобы использовать immutable.Queue
и var
в то же время! Например:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
var qr = q
var num = 0
while (!qr.isEmpty) {
val (digit, newQ) = qr.dequeue
num *= 10
num += digit
qr = newQ
}
num
}
Этот код по-прежнему эффективен, не требует рекурсии, и вам не нужно беспокоиться о том, делать копию своей очереди или нет перед вызовом toNum
, Естественно, я избегал повторного использования переменных для других целей, и ни один код за пределами этой функции их не видит, поэтому мне не нужно беспокоиться об изменении их значений от одной строки к другой - кроме случаев, когда я делаю это явно.
Scala решила позволить программисту сделать это, если программист посчитал это лучшим решением. Другие языки решили сделать такой код сложным. Цена, которую платит Scala (и любой язык с широко распространенной изменчивостью), заключается в том, что у компилятора не так много возможностей для оптимизации кода, как в противном случае. Ответ Java на это - оптимизация кода на основе профиля времени выполнения. Мы могли бы говорить о плюсах и минусах каждой стороны.
Лично я думаю, что Скала пока устанавливает правильный баланс. Это не идеально, безусловно. Я думаю, что и у Clojure, и у Haskell есть очень интересные понятия, не принятые Scala, но у Scala есть и свои сильные стороны. Посмотрим, что будет в будущем.
val
является окончательным, то есть не может быть установлен. Считать final
в Яве.
Проще говоря:
переменная = переменная
val = v ariable + fin al
val
означает неизменный и var
означает изменчивый.
Разница в том, что var
может быть переназначен, тогда как val
не могу. Изменчивость, или что-то иное из того, что фактически назначено, является побочной проблемой:
import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.
В то время как:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
И поэтому:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Если вы строите структуру данных и все ее поля val
s, то эта структура данных, следовательно, неизменна, так как ее состояние не может измениться.
Мышление с точки зрения C++,
val x: T
аналог константного указателя на непостоянные данные
T* const x;
в то время как
var x: T
аналог непостоянного указателя на непостоянные данные
T* x;
Отдавая val
над var
увеличивает неизменяемость кодовой базы, что может способствовать ее корректности, параллелизму и понятности.
"val означает неизменный, а var означает изменяемый".
Перефразируя, "val означает значение, а var означает переменную".
Различие, которое оказывается чрезвычайно важным в вычислениях (потому что эти два понятия определяют саму суть всего, что связано с программированием), и что ОО удалось размывать почти полностью, потому что в ОО единственная аксиома заключается в том, что "все является объект". И как следствие, многие программисты в наши дни склонны не понимать / ценить / распознавать, потому что им "промыли мозги" исключительно для "осмысления ОО-пути". Часто это приводит к тому, что переменные / изменяемые объекты используются, как и везде, когда значения / неизменяемые объекты могут / часто были бы лучше.
val означает неизменный, а var означает изменяемый
ты можешь думать val
как язык программирования Java final
ключевой мир или язык C++ const
ключевой мир。
Val
означает его окончательный, не может быть переназначен
В то время как, Var
может быть переназначен позже.
Val похож на конечную переменную в Java. После инициализации val никогда не может быть переназначен.
В отличие от этого, переменная подобна неконечной переменной в Java. VAR может быть переназначен на протяжении всей жизни.
Это так просто, как назвать.
var означает, что может меняться
val означает неизменный
Хотя многие уже ответили на разницу между Val и var. Но следует заметить, что val не совсем как конечное ключевое слово.
Мы можем изменить значение val с помощью рекурсии, но мы никогда не сможем изменить значение final. Финал более постоянен, чем Вал.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Параметры метода по умолчанию - val, и при каждом вызове значение изменяется.
С точки зрения javascript, это то же самое, что
val -> const переменная -> переменная
Val - значения являются типизированными константами хранения. После создания его значение не может быть переназначено. новое значение можно определить с помощью ключевого слова val.
например. val x: Int = 5
Здесь type является необязательным, поскольку scala может вывести его из заданного значения.
Переменные - это типизированные единицы хранения, которым можно снова присвоить значения, если зарезервировано место в памяти.
например. var x: Int = 5
Данные, хранящиеся в обоих хранилищах, автоматически удаляются JVM, когда они больше не нужны.
В scala значения предпочтительнее переменных из-за стабильности, что приводит к коду, особенно в параллельном и многопоточном коде.