Val-mutable versus var-immutable dans Scala

Existe-t-il des directives dans Scala sur l’utilisation de val avec une collection mutable ou l’utilisation de var avec une collection immuable? Ou devriez-vous vraiment viser val avec une collection immuable?

Le fait qu’il y ait deux types de collection me donne beaucoup de choix, et souvent je ne sais pas comment faire ce choix.

Question assez commune, celle-ci. La chose difficile est de trouver les doublons.

Vous devez rechercher la transparence référentielle . Cela signifie que si j’ai une expression “e”, je pourrais créer un val x = e et remplacer e par x . Ceci est la propriété que la mutabilité se brise. Lorsque vous devez prendre une décision de conception, optimisez la transparence référentielle.

En pratique, une variable locale à la méthode est la plus sûre qui existe, car elle n’échappe pas à la méthode. Si la méthode est courte, encore mieux. Si ce n’est pas le cas, essayez de le réduire en extrayant d’autres méthodes.

D’autre part, une collection mutable peut potentiellement s’échapper, même si ce n’est pas le cas. Lors de la modification du code, vous souhaiterez peut-être le transmettre à d’autres méthodes ou le renvoyer. C’est le genre de chose qui brise la transparence référentielle.

Sur un object (un champ), à peu près la même chose se produit, mais avec des conséquences plus graves. De toute façon l’object aura l’état et, par conséquent, rompre la transparence référentielle. Mais avoir une collection mutable signifie que même l’object lui-même peut perdre le contrôle de qui le change.

Si vous travaillez avec des collections immuables et que vous devez les “modifier”, par exemple en y ajoutant des éléments dans une boucle, vous devez utiliser var s car vous devez stocker la collection résultante quelque part. Si vous ne lisez que des collections immuables, utilisez alors val s.

En général, assurez-vous de ne pas confondre les références et les objects. val s sont des références immuables (pointeurs constants en C). C’est-à-dire que lorsque vous utilisez val x = new MutableFoo() , vous pourrez changer l’object val x = new MutableFoo() x , mais vous ne pourrez pas changer d’object x points. Le contraire est valable si vous utilisez var x = new ImmutableFoo() . Reprenant mon conseil initial: si vous n’avez pas besoin de changer d’object, utilisez les val s.

La meilleure façon de répondre à cela est un exemple. Supposons que nous ayons un processus simplement en recueillant des chiffres pour une raison quelconque. Nous souhaitons enregistrer ces numéros et enverrons la collecte à un autre processus pour ce faire.

Bien sûr, nous collectons toujours des numéros après l’envoi de la collection à l’enregistreur. Et supposons que le processus de journalisation entraîne des retards dans la journalisation. J’espère que vous pouvez voir où cela se passe.

Si nous stockons cette collection dans une valeur mutable (modifiable car nous y ajoutons continuellement), cela signifie que le processus effectuant la journalisation examinera le même object qui est encore mis à jour par notre processus de collecte. Cette collecte peut être mise à jour à tout moment, et lorsque le moment est venu de vous connecter, il est possible que nous ne consignions pas la collection que nous avons envoyée.

Si nous utilisons une variable immuable, nous envoyons une structure de données immuable au consignateur. Lorsque nous appendons plus de nombres à notre collection, nous remplacerons notre var par une nouvelle structure de données immuable . Cela ne signifie pas que la collecte envoyée à l’enregistreur est remplacée! Il fait toujours référence à la collection qui a été envoyée. Ainsi, notre enregistreur enregistrera bien la collection reçue.

Je pense que les exemples de cet article de blog apporteront plus de lumière, car la question de savoir quelle combinaison utiliser devient encore plus importante dans les scénarios de concurrence: importance de l’immuabilité pour la concurrence . Et pendant que nous y sums, notez l’utilisation préférée de synchronized vs @volatile vs quelque chose comme AtomicReference: trois outils

var immutable vs val mutable

En plus de nombreuses excellentes réponses à cette question. Voici un exemple simple, qui illustre les dangers potentiels de la mutabilité du val mutable :

Les objects mutables peuvent être modifiés à l’intérieur des méthodes, qui les prennent comme parameters, tandis que la réaffectation n’est pas autorisée.

 import scala.collection.mutable.ArrayBuffer object MyObject { def main(args: Array[Ssortingng]) { val a = ArrayBuffer(1,2,3,4) silly(a) println(a) // a has been modified here } def silly(a: ArrayBuffer[Int]): Unit = { a += 10 println(s"length: ${a.length}") } } 

Résultat:

 length: 5 ArrayBuffer(1, 2, 3, 4, 10) 

Quelque chose comme cela ne peut pas se produire avec var immutable , car la réaffectation n’est pas autorisée:

 object MyObject { def main(args: Array[Ssortingng]) { var v = Vector(1,2,3,4) silly(v) println(v) } def silly(v: Vector[Int]): Unit = { v = v :+ 10 // This line is not valid println(s"length of v: ${v.length}") } } 

Résulte en:

 error: reassignment to val 

Les parameters de fonction étant traités comme val cette réaffectation n’est pas autorisée.