В чем разница между определениями var
и val
в Scala и почему нужен язык для обоих? Почему вы выбрали val
над a var
и наоборот?
В чем разница между определением var и val в Scala?
Ответ 1
Как и многие другие, объект, назначенный 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 имеет свои сильные стороны. Мы увидим, что происходит в будущем.
Ответ 2
val
является окончательным, то есть не может быть задано. Подумайте final
в java.
Ответ 3
Проще говоря:
переменная= переменная iable
val= v ariable + fin al
Ответ 4
Разница в том, что 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, то эта структура данных поэтому неизменяема, так как ее состояние не может измениться.
Ответ 5
val
означает неизменяемый и var
означает изменчивый.
Ответ 6
Мышление в терминах C++,
val x: T
аналог константного указателя на непостоянные данные
T* const x;
в то время как
var x: T
аналог непостоянного указателя на непостоянные данные
T* x;
Использование val
сравнению с var
увеличивает неизменяемость кодовой базы, что может способствовать ее корректности, параллелизму и понятности.
Чтобы понять смысл наличия постоянного указателя на непостоянные данные, рассмотрим следующий фрагмент Scala:
val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)
Здесь "указатель" val m
является константой, поэтому мы не можем переназначить его так, чтобы он указывал на что-то подобное
m = n // error: reassignment to val
однако мы действительно можем изменить сами непостоянные данные, которые m
указывает, как так
m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)
Ответ 7
"val означает неизменяемый и var означает mutable."
Чтобы перефразировать, значение val означает значение, а var означает переменную ".
Различие, которое имеет чрезвычайно важное значение при вычислении (поскольку эти два понятия определяют саму суть программирования) и что OO удалось полностью размыть, поскольку в OO единственная аксиома заключается в том, что "все это объект". И, как следствие, многие программисты в наши дни, как правило, не понимают/ценят/не признают, потому что им "промывают мозги" исключительно "мышлением OO". Часто приводя к переменным/изменяемым объектам, которые используются как везде, когда значения/неизменяемые объекты могли/часто были лучше.
Ответ 8
val означает неизменяемое, а var означает mutable
вы можете думать val
как язык Java-программирования final
ключевой мир или язык С++ языка const
.
Ответ 9
A val похож на конечную переменную в Java. После инициализации val никогда не может быть переназначена.
A var, напротив, похож на не конечную переменную в Java. var можно переназначить на протяжении всего срока его службы.
Ответ 10
Это так просто, как это называется.
var означает, что он может меняться
val означает неизменное
Ответ 11
Значения Val - это типизированные константы хранения. После создания его значение не может быть повторно назначено. новое значение может быть определено с помощью ключевого слова val.
например. val x: Int = 5
Здесь тип необязателен, поскольку scala может вывести его из назначенного значения.
Var - переменные представляют собой типизированные единицы хранения, которые могут быть назначены значения снова, пока зарезервировано пространство памяти.
например. var x: Int = 5
Данные, хранящиеся в обоих блоках памяти, автоматически де-распределяются JVM, если они больше не нужны.
В scala значения предпочтительны по сравнению с переменными из-за стабильности, которые приводят к коду, в частности, в параллельном и многопоточном коде.
Ответ 12
Хотя многие уже ответили на разницу между Val и var. Но следует заметить, что val не совсем как конечное ключевое слово.
Мы можем изменить значение val с помощью рекурсии, но мы никогда не сможем изменить значение final. Финал более постоянен, чем Вал.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Параметры метода по умолчанию имеют значение val и при каждом вызове значение изменяется.
Ответ 13
Val
означает его окончательный, не может быть переназначен
Принимая во внимание, что Var
может быть переназначен позже.