В чем разница между определением var и val в Scala?

В чем разница между определениями var и val в Scala и почему нужен язык для обоих? Почему вы выбрали val над a var и наоборот?

Ответы

Ответ 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, то эта структура данных поэтому неизменяема, так как ее состояние не может измениться.

Ответ 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 может быть переназначен позже.