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

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

14 ответов

Решение

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

val является окончательным, то есть не может быть установлен. Считать final в Яве.

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

переменная = переменная

val = v ariable + fin al

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

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

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

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

val x: T

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

T* const x;

в то время как

var x: T 

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

T* x;

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

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

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

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

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

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

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

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

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

В отличие от этого, переменная подобна неконечной переменной в Java. VAR может быть переназначен на протяжении всей жизни.

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

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

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

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

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

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

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

С точки зрения javascript, это то же самое, что

val -> const переменная -> переменная

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

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

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

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

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

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

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

Другие вопросы по тегам