web-dev-qa-db-fra.com

Quelle est la différence entre une définition de var et val dans Scala?

Quelle est la différence entre une définition var et val dans Scala et pourquoi la langue a-t-elle besoin des deux? Pourquoi choisiriez-vous une val par rapport à une var et vice versa?

282
Derek Mahar

Comme tant d'autres l'ont dit, l'objet assigné à une val ne peut pas être remplacé, et l'objet assigné à une var peut. Cependant, cet objet peut avoir son état interne modifié. Par exemple:

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.
  }
}

Ainsi, même si nous ne pouvons pas changer l'objet assigné à x, nous pourrions changer l'état de cet objet. À la base, cependant, il y avait une var.

Maintenant, l'immuabilité est une bonne chose pour plusieurs raisons. Tout d'abord, si un objet ne change pas son état interne, vous n'avez pas à vous inquiéter si une autre partie de votre code le modifie. Par exemple:

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")

Cela devient particulièrement important avec les systèmes multithread. Dans un système multithread, les problèmes suivants peuvent survenir:

x = new B(1)
f(x)
if (x.value.value == 1) {
  print(x.value.value) // Can be different than 1!
}

Si vous utilisez exclusivement val et n'utilisez que des structures de données immuables (c'est-à-dire, évitez les tableaux, tout ce qui est dans scala.collection.mutable, etc.), vous pouvez être assuré que cela n'arrivera pas. C’est-à-dire que, sauf s’il existe du code, voire même un cadre, faisant des tours de réflexion, la réflexion peut changer des valeurs "immuables", malheureusement.

C'est une des raisons, mais il y en a une autre. Lorsque vous utilisez var, vous pouvez être tenté de réutiliser le même var à plusieurs fins. Cela a quelques problèmes:

  • Les personnes qui liront le code auront plus de difficulté à savoir quelle est la valeur d'une variable dans une certaine partie du code.
  • Vous pouvez oublier de réinitialiser la variable dans un chemin de code et finissez par transmettre des valeurs erronées en aval dans le code.

Autrement dit, utiliser val est plus sûr et conduit à un code plus lisible.

Nous pouvons alors aller dans l'autre sens. Si val est-ce mieux, pourquoi avoir var du tout? Certaines langues ont effectivement emprunté cette voie, mais il existe des situations dans lesquelles la mutabilité améliore considérablement les performances.

Par exemple, prenons une Queue immuable. Lorsque vous ajoutez des éléments enqueue ou dequeue, vous obtenez un nouvel objet Queue. Comment alors, iriez-vous sur le traitement de tous les articles qu'il contient?

Je vais passer à travers cela avec un exemple. Supposons que vous avez une file de chiffres et que vous souhaitez en composer un numéro. Par exemple, si j'ai une file d'attente avec 2, 1, 3, dans cet ordre, je veux récupérer le nombre 213. Commençons par le résoudre avec un mutable.Queue:

def toNum(q: scala.collection.mutable.Queue[Int]) = {
  var num = 0
  while (!q.isEmpty) {
    num *= 10
    num += q.dequeue
  }
  num
}

Ce code est rapide et facile à comprendre. Son principal inconvénient est que la file d'attente transmise est modifiée par toNum. Vous devez donc en faire une copie au préalable. C'est le genre de gestion d'objet que l'immutabilité vous rend libre.

Maintenant, convertissons-le en un 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)
}

Comme je ne peux pas réutiliser certaines variables pour garder une trace de ma num, comme dans l'exemple précédent, je dois recourir à la récursion. Dans ce cas, il s’agit d’une récursion de la queue, qui a de très bonnes performances. Mais ce n'est pas toujours le cas: parfois, il n'y a pas de bonne solution de récursion de la queue (lisible, simple).

Notez cependant que je peux réécrire ce code pour utiliser un immutable.Queue et un var en même temps! Par exemple:

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
}

Ce code est toujours efficace, ne nécessite pas de récursivité et vous n'avez pas à vous demander si vous devez copier votre file d'attente ou non avant d'appeler toNum. Naturellement, j'ai évité de réutiliser des variables à d'autres fins, et aucun code en dehors de cette fonction ne les voit, je n'ai donc pas à m'inquiéter du fait que leurs valeurs changent d'une ligne à l'autre - sauf lorsque je le fais explicitement.

Scala a choisi de laisser le programmeur faire cela, si le programmeur estimait que c'était la meilleure solution. D'autres langues ont choisi de rendre ce code difficile. Le prix payé par Scala (et toute langue à mutabilité étendue) est que le compilateur n'a pas autant de marge de manœuvre pour optimiser le code qu'il ne le ferait autrement. La réponse de Java à cela consiste à optimiser le code en fonction du profil d'exécution. Nous pourrions continuer encore et encore sur les avantages et les inconvénients de chaque côté.

Personnellement, je pense que Scala trouve le bon équilibre, pour le moment. Ce n'est pas parfait, de loin. Je pense que Clojure et Haskell ont des notions très intéressantes qui n’ont pas été adoptées par Scala, mais Scala a aussi ses propres forces. Nous verrons ce qui va arriver dans le futur.

312
Daniel C. Sobral

val est final, c'est-à-dire qu'il ne peut pas être défini. Pensez final en Java.

57
Jackson Davis

En termes simples:

var = var iable

val = v ariable + fin al

43
Ajay Gupta

La différence est qu’une var peut être réaffectée à une val ne le peut pas. La mutabilité, ou autre chose de ce qui est réellement assigné, est un problème secondaire:

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.

Tandis que:

val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.

Et donc:

val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.

Si vous construisez une structure de données et que tous ses champs sont vals, cette structure de données est donc immuable, son état ne pouvant pas changer.

20
oxbow_lakes

val signifie immuable et var signifie mutable.

Discussion complète.

19
Stefan Kendall

Penser en termes de C++, 

val x: T

est analogue au pointeur constant sur des données non constantes

T* const x;

tandis que 

var x: T 

est analogue au pointeur non constant sur des données non constantes 

T* x;

Privilégier val par rapport à var augmente l’immuabilité de la base de code, ce qui peut en faciliter l’exactitude, la concurrence et la compréhensibilité.

11
Mario Galic

"val signifie immuable et var signifie mutable."

Pour paraphraser, "val signifie valeur et var signifie variable".

Une distinction qui s'avère extrêmement importante en informatique (car ces deux concepts définissent l'essence même de la programmation) et que OO a réussi à brouiller presque complètement, car dans OO, le seul axiome est "tout est un objet". Et en conséquence, beaucoup de programmeurs de nos jours ont tendance à ne pas comprendre/apprécier/reconnaître, car ils ont été soumis à un lavage de cerveau consistant à "penser de la manière OO" exclusivement. Cela conduit souvent à l'utilisation d'objets variables/mutables comme partout , lorsque les objets de valeur/immuables pourraient/auraient souvent été meilleurs.

8
Erwin Smout

val signifie immuable et var signifie mutable

vous pouvez penser val comme langage de programmation Java final clé monde ou c ++ langage const clé monde

6
Rollen Holt

C'est aussi simple que son nom. 

var signifie que cela peut varier

val signifie invariable

1
user105003

Bien que beaucoup aient déjà répondu à la différence entre Val et var . Mais il convient de noter que val ne correspond pas exactement à final mot clé.

Nous pouvons changer la valeur de val en utilisant la récursivité mais nous ne pouvons jamais changer la valeur de final. La finale est plus constante que Val.

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

Les paramètres de méthode sont par défaut val et à chaque appel, la valeur est modifiée. 

0

Les valeurs Val sont des constantes de stockage typées. Une fois créée, sa valeur ne peut pas être réaffectée. une nouvelle valeur peut être définie avec le mot-clé val.

par exemple. val x: Int = 5

Ici, le type est facultatif car scala peut le déduire de la valeur attribuée.

Var - Les variables sont des unités de stockage typées auxquelles on peut attribuer à nouveau des valeurs tant que l'espace mémoire est réservé. 

par exemple. var x: Int = 5

Les données stockées dans les deux unités de stockage sont automatiquement désallouées par JVM dès qu'elles ne sont plus nécessaires.

Dans scala, les valeurs sont préférées aux variables en raison de la stabilité qu'elles apportent au code, en particulier dans le code simultané et multithread.

0
SunTech

Val signifie son final , ne peut pas être réaffecté

Alors que, Var peut être réaffecté plus tard .

0
Crime_Master_GoGo