Quelle est la différence entre a var
et val
définition en Scala et pourquoi la langue a-t-elle besoin des deux? Pourquoi choisiriez-vous un val
sur un var
et vice versa?
Comme beaucoup d'autres l'ont dit, l'objet affecté à un val
ne peut pas être remplacé et l'objet affecté à une var
boîte. Cependant, ledit objet peut voir 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.
}
}
Donc, même si nous ne pouvons pas changer l'objet assigné x
, nous pouvons changer l'état de cet objet. À l'origine, cependant, il y avait un var
.
Maintenant, l'immuabilité est une bonne chose pour de nombreuses raisons. Tout d'abord, si un objet ne change pas d'état interne, vous n'avez pas à vous inquiéter si une autre partie de votre code le change. 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 événements suivants peuvent se produire:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Si vous utilisez val
exclusivement et n'utilisez que des structures de données immuables (c'est-à-dire, évitez les tableaux, tout ce qui s'y trouve scala.collection.mutable
, etc.), vous pouvez être assuré que cela ne se produira pas. Autrement dit, à moins qu'il y ait du code, peut-être même un cadre, faisant des tours de réflexion - la réflexion peut malheureusement changer les valeurs "immuables".
C'est une raison, 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, l'utilisation val
est plus sûre et conduit à un code plus lisible.
On peut alors aller dans l'autre sens. Si val
c'est mieux, pourquoi en avoir var
? Eh bien, certains langages ont choisi cette voie, mais il existe des situations dans lesquelles la mutabilité améliore considérablement les performances.
Par exemple, prenez un immuable Queue
. Lorsque vous enqueue
ou des dequeue
objets, vous obtenez un nouvel Queue
objet. Comment allez-vous alors traiter tous les éléments qu'il contient?
Je vais passer par là avec un exemple. Disons que vous avez une file d'attente de chiffres et que vous souhaitez en composer un nombre. Par exemple, si j'ai une file d'attente avec 2, 1, 3, dans cet ordre, je veux récupérer le nombre 213. Résolvons-le d'abord 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 qui est passée est modifiée par toNum
, vous devez donc en faire une copie au préalable. C'est le type de gestion d'objets dont l'immuabilité vous libère.
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)
}
Parce que je ne peux pas réutiliser une variable pour garder une trace de ma num
, comme dans l'exemple précédent, j'ai besoin de recourir à la récursivité. Dans ce cas, il s'agit d'une récursion de queue, qui a de très bonnes performances. Mais ce n'est pas toujours le cas: parfois, il n'y a tout simplement pas de bonne solution de récursion de 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 soucier de savoir si vous devez faire une copie de 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, donc je n'ai pas besoin de m'inquiéter de voir leurs valeurs changer d'une ligne à l'autre - sauf lorsque je le fais explicitement.
Scala a choisi de laisser le programmeur faire cela, si le programmeur jugeait que c'était la meilleure solution. D'autres langues ont choisi de rendre ce code difficile. Le prix que Scala (et tout langage avec une mutabilité généralisée) paie est que le compilateur n'a pas autant de latitude pour optimiser le code qu'il pourrait autrement. La réponse de Java à cela est l'optimisation du code basé sur le profil d'exécution. Nous pourrions continuer indéfiniment sur les avantages et les inconvénients de chaque côté.
Personnellement, je pense que Scala trouve le bon équilibre, pour l'instant. 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 se prépare pour l'avenir.
var qr = q
une copie deq
?q
. Il fait une copie - sur la pile, pas le tas - de la référence à cet objet. En ce qui concerne les performances, vous devrez être plus clair sur ce dont vous parlez.(x::xs).drop(1)
c'est exactementxs
, pas une "copie" dexs
) ce lien que je pourrais comprendre. tnx!qr
s'agit d'une file d'attente immuable, chaque fois que l'expressionqr.dequeue
est appelée, elle crée unnew Queue
(voir < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).val
est définitive, c'est-à-dire qu'elle ne peut pas être définie. Pensezfinal
en java.la source
val
variables sont immuables, mais les objets qu'elles référencent ne doivent pas nécessairement l'être. Selon le lien publié par Stefan: "Ici, la référence des noms ne peut pas être modifiée pour pointer vers un tableau différent, mais le tableau lui-même peut être modifié. En d'autres termes, le contenu / les éléments du tableau peuvent être modifiés." C'est donc comme celafinal
fonctionne en Java.+=
à une table de hachage mutable définie commeval
très bien - je crois que c'est exactement comment celafinal
fonctionne en javaEn termes simples:
var = var iable
val = v ariable + fin al
la source
La différence est que a
var
peut être réaffecté à alors queval
ne peut pas. La mutabilité, ou autre de ce qui est réellement attribué, est un problème secondaire:Tandis que:
Et donc:
Si vous construisez une structure de données et que tous ses champs sont
val
s, cette structure de données est donc immuable, car son état ne peut pas changer.la source
val
signifie immuable etvar
signifie mutable.Discussion complète.
la source
Penser en termes de C ++,
est analogue à un pointeur constant vers des données non constantes
tandis que
est analogue à un pointeur non constant vers des données non constantes
privilégiant
val
survar
augmente immuabilité de la base de code qui peut faciliter son exactitude, la simultanéité et compréhensible.Pour comprendre la signification d'avoir un pointeur constant vers des données non constantes, considérez l'extrait de code Scala suivant:
Ici, le "pointeur"
val m
est constant, nous ne pouvons donc pas le réaffecter pour pointer vers autre chose comme çamais nous pouvons en effet changer les données non constantes elles-mêmes qui
m
pointent comme sila source
"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 en OO, le seul axiome est que "tout est un objet". Et qu'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 pour "penser à la manière OO" exclusivement. Menant souvent à l'utilisation d'objets variables / mutables comme partout , alors que la valeur / les objets immuables auraient pu / auraient souvent été mieux.
la source
vous pouvez penser
val
commefinal
monde clé du langage de programmation java ou monde clé du langage c ++const
。la source
Val
signifie sa finale , ne peut pas être réaffectéeAttendu que,
Var
peut être réaffecté plus tard .la source
C'est aussi simple que son nom.
la source
Val - les valeurs 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 affectée.
Var - les variables sont des unités de stockage typées auxquelles des valeurs peuvent être attribuées à nouveau 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.
En 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.
la source
Bien que beaucoup aient déjà répondu à la différence entre Val et var . Mais un point à noter est que val n'est pas exactement comme 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.
Les paramètres de méthode sont par défaut val et à chaque appel la valeur est modifiée.
la source