В чем разница между a var
и val
определением в Scala и почему язык нуждается в обоих? Почему вы выбрали бы val
более var
и наоборот?
Как говорили многие другие, объект, назначенный val
элементу, не может быть заменен, а объект, назначенный элементу var
can. Однако у указанного объекта может быть изменено его внутреннее состояние. Например:
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 на это - оптимизация кода на основе профиля времени выполнения. Мы могли бы говорить о плюсах и минусах каждой стороны.
Лично я думаю, что Скала пока устанавливает правильный баланс. Это не идеально, безусловно. Я думаю, что и у Clojure, и у Haskell есть очень интересные понятия, не принятые Scala, но у Scala есть и свои сильные стороны. Посмотрим, что будет в будущем.
var qr = q
копияq
?q
. Он делает копию - в стеке, а не в куче - ссылки на этот объект. Что касается производительности, вам нужно будет более четко понимать, о чем «это» вы говорите.(x::xs).drop(1)
это точноxs
, а не «копия»xs
) отсюда ссылка, которую я мог понять. TNX!qr
является неизменной, каждый раз, когдаqr.dequeue
вызывается выражение, оно создаетnew Queue
(см. < Github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).val
является окончательным, то есть не может быть установлен. Думайfinal
в яве.источник
val
переменные являются неизменяемыми, но объекты, на которые они ссылаются, необязательно должны быть. По ссылке Стефан написал: «Здесь ссылка на имена не может быть изменена, чтобы указывать на другой массив, но сам массив может быть изменен. Другими словами, содержимое / элементы массива могут быть изменены». Так что это похоже на то, какfinal
работает в Java.+=
изменяемый hashmap, определенный как оченьval
хороший - я верю, что именно такfinal
работает в JavaПроще говоря:
вар = переменная iable
val = v ariable + fin al
источник
Разница в том, что а
var
можно переназначить, аval
нельзя. Изменчивость, или что-то еще из того, что фактически назначено, является побочной проблемой:В то время как:
И поэтому:
Если вы строите структуру данных и все ее поля имеют значение
val
s, то эта структура данных является неизменной, поскольку ее состояние не может измениться.источник
val
означает неизменный иvar
означает изменчивый.Полное обсуждение.
источник
Мышление с точки зрения C ++,
аналог константного указателя на непостоянные данные
пока
аналог непостоянного указателя на непостоянные данные
Любить
val
болееvar
увеличивает неизменность кодовой базы, что может способствовать его правильности, параллелизму и понятности.Чтобы понять смысл наличия постоянного указателя на непостоянные данные, рассмотрим следующий фрагмент Scala:
Здесь «указатель»
val m
является постоянным, поэтому мы не можем переназначить его так, чтобы он указывал на что-то подобноеОднако мы действительно можем изменить сами непостоянные данные, которые
m
указывают на какисточник
«val означает неизменный, а var означает изменяемый».
Перефразируя, «val означает значение, а var означает переменную».
Различие, которое оказывается чрезвычайно важным в вычислениях (потому что эти два понятия определяют саму суть всего, что связано с программированием), и что ОО удалось почти полностью размывать, потому что в ОО единственная аксиома заключается в том, что «все является объект». И как следствие, многие программисты в наши дни склонны не понимать / ценить / распознавать, потому что им «промыли мозги» исключительно для «осмысления ОО-пути». Часто это приводит к тому, что переменные / изменяемые объекты используются, как и везде , когда значения / неизменяемые объекты могут / часто были бы лучше.
источник
Вы можете думать
val
какfinal
ключевой мир языка программирования Java илиconst
ключевой мир языка C ++ 。источник
Val
значит его окончательный , нельзя переназначенПринимая во внимание, что
Var
может быть переназначен позже .источник
Это так просто, как назвать.
источник
Val - значения являются типизированными константами хранения. После создания его значение не может быть переназначено. новое значение можно определить с помощью ключевого слова val.
например. val x: Int = 5
Здесь type является необязательным, поскольку scala может вывести его из заданного значения.
Переменные - это типизированные единицы хранения, которым можно снова присвоить значения, если зарезервировано место в памяти.
например. var x: Int = 5
Данные, хранящиеся в обеих единицах хранения, автоматически удаляются JVM, когда они больше не нужны.
В scala значения предпочтительнее переменных из-за стабильности, что приводит к коду, особенно в параллельном и многопоточном коде.
источник
Хотя многие уже ответили на разницу между Val и var . Но следует отметить, что val не совсем как final ключевое слово.
Мы можем изменить значение val с помощью рекурсии, но мы никогда не сможем изменить значение final. Финал более постоянен, чем Вал.
Параметры метода по умолчанию - val, и при каждом вызове значение изменяется.
источник