В чем разница между определениями var
и val
в Scala и почему нужен язык для обоих? Почему вы выбрали val
над a var
и наоборот?
Как и многие другие, объект, назначенный 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
и a 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 на это - оптимизация кода на основе профиля времени выполнения. Мы могли бы продолжать и обсуждать все плюсы и минусы с каждой стороны.
Лично я думаю, что Scala покажет правильный баланс. Это далеко не идеальный вариант. Я думаю, что Clojure и Haskell имеют очень интересные понятия, не принятые Scala, но Scala имеет свои сильные стороны. Мы увидим, что происходит в будущем.
val
является окончательным, то есть не может быть задано. Подумайте final
в java.
val
являются неизменяемыми, но объекты, на которые они ссылаются, не обязательно должны быть. По ссылке Стефан написал: «Здесь ссылка на имена не может быть изменена, чтобы указывать на другой массив, но сам массив может быть изменен. Другими словами, содержимое / элементы массива могут быть изменены». Так что это похоже на то, как final
работает в Java.
+=
на mutabled hashmap, определенном как val
просто отлично - я верю, что именно так работает final
в java
Простыми словами:
var= var iable
val= va reiable + fin al
Разница в том, что a var
может быть повторно назначено, тогда как a 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, то эта структура данных поэтому неизменяема, так как ее состояние не может измениться.
val
означает неизменяемый и var
означает изменчивый.
Думая о C++,
val x: T
аналогичен постоянному указателю на непостоянные данные
T* const x;
while
var x: T
аналогичен непостоянному указателю на непостоянные данные
T* x;
Приоритет val
над var
увеличивает неизменность кодовой базы, которая может облегчить ее правильность, concurrency и понятность.
"val означает неизменяемый и var означает mutable."
Чтобы перефразировать, значение val означает значение, а var означает переменную ".
Различие, которое имеет чрезвычайно важное значение при вычислении (поскольку эти два понятия определяют саму суть программирования) и что OO удалось полностью размыть, поскольку в OO единственная аксиома заключается в том, что "все это объект". И, как следствие, многие программисты в наши дни, как правило, не понимают/ценят/не признают, потому что им "промывают мозги" исключительно "мышлением OO". Часто приводя к переменным/изменяемым объектам, которые используются как везде, когда значения/неизменяемые объекты могли/часто были лучше.
val означает неизменяемое, а var означает mutable
вы можете думать val
как язык Java-программирования final
ключевой мир или язык С++ языка const
.
A val похож на конечную переменную в Java. После инициализации val никогда не может быть переназначена.
A var, напротив, похож на не конечную переменную в Java. var можно переназначить на протяжении всего срока его службы.
Это так просто, как это называется.
var означает, что он может меняться
val означает неизменное
Значения Val - это типизированные константы хранения. После создания его значение не может быть повторно назначено. новое значение может быть определено с помощью ключевого слова val.
например. val x: Int = 5
Здесь тип необязателен, поскольку scala может вывести его из назначенного значения.
Var - переменные представляют собой типизированные единицы хранения, которые могут быть назначены значения снова, пока зарезервировано пространство памяти.
например. var x: Int = 5
Данные, хранящиеся в обоих блоках памяти, автоматически де-распределяются JVM, если они больше не нужны.
В scala значения предпочтительны по сравнению с переменными из-за стабильности, которые приводят к коду, в частности, в параллельном и многопоточном коде.
var qr = q
копиюq
?