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

Наиболее заметное отличие между и неподписанными блоками со знаком находится в приложении ClickOnce. Если Вы не подпишете его, то пользователи получат страшного "Неизвестного Издателя" предупреждение диалогового окна в первый раз, когда они запускают Ваше приложение. Если Вы подписали его с сертификат от доверяемых полномочий , то они видят диалоговое окно, это менее страшно. Насколько я знаю, подписываясь с сертификатом, который Вы генерируете сами, не влияет на "Неизвестного Издателя" предупреждение. Момент SSL от Comodo имеет примеры диалоговых окон.

существуют некоторые более тонкие различия. Необходимо подписать блок, прежде чем он сможет быть установлен в глобальном кэше сборок (GAC), где он может быть совместно использован несколькими приложениями. Подписание является неотъемлемой частью безопасности доступа к коду (CAS), но я не нашел никого, кто мог получить работу CAS. Я вполне уверен, что и GAC и CAS хорошо работают с сертификатами, которые Вы генерируете сами.

289
задан Derek Mahar 11 December 2009 в 07:15
поделиться

5 ответов

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

Теперь давайте скроем его в неизменяемый.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 , как и в предыдущем примере, мне нужно прибегнуть к рекурсии. В данном случае это хвостовая рекурсия, которая имеет довольно хорошую производительность. Но это не всегда так: иногда просто нет хорошего (читаемого, простого) решения хвостовой рекурсии.

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

322
ответ дан 23 November 2019 в 01:45
поделиться

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

57
ответ дан 23 November 2019 в 01:45
поделиться

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

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

20
ответ дан 23 November 2019 в 01:45
поделиться

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

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

19
ответ дан 23 November 2019 в 01:45
поделиться

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

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

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

8
ответ дан 23 November 2019 в 01:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: