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?
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:
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.
val
est final, c'est-à-dire qu'il ne peut pas être défini. Pensez final
en Java.
En termes simples:
var = var iable
val = v ariable + fin al
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 val
s, cette structure de données est donc immuable, son état ne pouvant pas changer.
val
signifie immuable et var
signifie mutable.
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é.
"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.
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
C'est aussi simple que son nom.
var signifie que cela peut varier
val signifie invariable
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.
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.
Val
signifie son final , ne peut pas être réaffecté
Alors que, Var
peut être réaffecté plus tard .