it-swarm.com.ru

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

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

282
Derek Mahar

Как уже говорили многие другие, объект, назначенный для 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 также есть свои сильные стороны. Посмотрим, что будет в будущем.

312
Daniel C. Sobral

val является окончательным, то есть не может быть установлен. Подумайте final в Java.

57
Jackson Davis

Проще говоря:

var = var iable

val = v ariable + fin al

43
Ajay Gupta

Разница в том, что 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.

Если вы строите структуру данных и все ее поля являются vals, то эта структура данных является неизменной, поскольку ее состояние не может измениться.

20
oxbow_lakes

val означает неизменяемый, а var означает изменяемый.

Полное обсуждение.

19
Stefan Kendall

Мышление с точки зрения C++, 

val x: T

аналог константного указателя на непостоянные данные

T* const x;

в то время как 

var x: T 

аналог непостоянного указателя на непостоянные данные 

T* x;

Использование val по сравнению с var увеличивает неизменяемость кодовой базы, что может способствовать ее правильности, параллелизму и понятности.

11
Mario Galic

«val означает неизменный, а var означает изменяемый».

Перефразируя, «val означает значение, а var означает переменную».

Различие, которое оказывается чрезвычайно важным в вычислениях (потому что эти два понятия определяют саму суть всего, что связано с программированием), и что OO удалось размывать почти полностью, потому что в ОО единственной аксиомой является то, что «все является объектом». И как следствие, многие программисты в наши дни склонны не понимать/ценить/распознавать, потому что им промывали мозги исключительно для «мышления по OO пути». Часто это приводит к тому, что переменные/изменяемые объекты используются, как везде , когда значения/неизменяемые объекты могут/часто были бы лучше.

8
Erwin Smout

val означает неизменный, а var означает изменяемый

вы можете думать val как язык программирования Java final мир ключей или язык c ++ const мир ключей。

6
Rollen Holt

Это так просто, как назвать. 

var означает, что может меняться

val означает неизменный

1
user105003

Хотя многие уже ответили на разницу между Val и var . Но следует заметить, что val не совсем то же самое, что и ключевое слово final .

Мы можем изменить значение val с помощью рекурсии, но мы никогда не сможем изменить значение final. Финал более постоянен, чем Вал.

def factorial(num: Int): Int = {
 if(num == 0) 1
 else factorial(num - 1) * num
}

Параметры метода по умолчанию - val, и при каждом вызове значение изменяется. 

0
Mahesh Chand Kandpal

Val - значения являются типизированными константами хранения. После создания его значение не может быть переназначено. новое значение можно определить с помощью ключевого слова val.

например. val x: Int = 5

Здесь type является необязательным, поскольку scala может вывести его из заданного значения.

Переменные - это типизированные единицы хранения, которым можно снова присвоить значения, если зарезервировано место в памяти. 

например. var x: Int = 5

Данные, хранящиеся в обеих единицах хранения, автоматически удаляются JVM, когда они больше не нужны.

В scala значения предпочтительнее переменных из-за стабильности, что приводит к коду, особенно в параллельном и многопоточном коде.

0
SunTech

Val означает, что его final , не может быть переназначен

Принимая во внимание, что Var может быть переназначен позже .

0
Crime_Master_GoGo