val-mutable contro var immutable in Scala

Ci sono delle linee guida in Scala su quando usare val con una collezione mutevole contro l’uso di var con una collezione immutabile? O dovresti davvero mirare a val con una collezione immutabile?

Il fatto che ci siano entrambi i tipi di raccolta mi dà molta scelta, e spesso non so come fare questa scelta.

Domanda abbastanza comune, questa. La cosa difficile è trovare i duplicati.

Dovresti cercare la trasparenza referenziale . Ciò significa che, se ho un’espressione “e”, potrei fare una val x = e , e sostituire e con x . Questa è la proprietà che interrompe la mutevolezza. Ogni volta che è necessario prendere una decisione progettuale, massimizzare la trasparenza referenziale.

In pratica, un metodo- var locale è la var più sicura che esista, dal momento che non sfugge al metodo. Se il metodo è breve, ancora meglio. Se non lo è, prova a ridurlo estraendo altri metodi.

D’altra parte, una collezione mutevole ha il potenziale per sfuggire, anche se non lo è. Quando si modifica il codice, è ansible passare ad altri metodi o restituirlo. Questo è il tipo di cosa che infrange la trasparenza referenziale.

Su un object (un campo) succede più o meno la stessa cosa, ma con conseguenze più disastrose. In entrambi i casi l’object avrà lo stato e, quindi, interromperà la trasparenza referenziale. Ma avere una collezione mutevole significa che anche l’object stesso potrebbe perdere il controllo su chi lo sta cambiando.

Se lavori con collezioni immutabili e devi “modificarle”, ad esempio, aggiungi elementi in un ciclo, devi usare var s perché devi memorizzare la raccolta risultante da qualche parte. Se leggi solo da collezioni immutabili, usa val s.

In generale, assicurati di non confondere riferimenti e oggetti. val s sono riferimenti immutabili (puntatori costanti in C). Cioè, quando usi val x = new MutableFoo() , sarai in grado di cambiare l’object a cui punta x , ma non sarai in grado di cambiare a quale object x punti. Il contrario vale se usi var x = new ImmutableFoo() . Raccogliendo il mio consiglio iniziale: se non hai bisogno di cambiare a quale object un punto di riferimento, usa val s.

Il modo migliore per rispondere è con un esempio. Supponiamo di avere qualche processo semplicemente raccogliendo numeri per qualche motivo. Desideriamo registrare questi numeri e invieremo la raccolta a un altro processo per farlo.

Naturalmente, stiamo ancora raccogliendo i numeri dopo aver inviato la raccolta al registratore. E diciamo che c’è un sovraccarico nel processo di registrazione che ritarda la registrazione effettiva. Spero che tu possa vedere dove sta andando.

Se archiviamo questa raccolta in un valore mutabile, (mutabile perché la stiamo aggiungendo continuamente), ciò significa che il processo che esegue la registrazione guarderà lo stesso object che è ancora in fase di aggiornamento dal nostro processo di raccolta. Quella collezione può essere aggiornata in qualsiasi momento, e quindi quando è il momento di loggare potremmo non registrare la collezione che abbiamo inviato.

Se usiamo una var immutabile, inviamo una struttura di dati immutabile al registratore. Quando aggiungiamo più numeri alla nostra collezione, sostituiremo la nostra var con una nuova struttura di dati immutabile . Questo non significa che la raccolta inviata al registratore è stata sostituita! Fa ancora riferimento alla collezione che è stata inviata. Quindi il nostro registratore registrerà effettivamente la collezione che ha ricevuto.

Penso che gli esempi in questo post del blog saranno più chiari, poiché la domanda su quale combo utilizzare diventa ancora più importante negli scenari di concorrenza: importanza dell’immutabilità per la concorrenza . E mentre ci siamo, si noti l’uso preferito di sincronizzato vs @volatile vs qualcosa come AtomicReference: tre strumenti

var immutable vs. val mutable

Oltre a molte ottime risposte a questa domanda. Ecco un semplice esempio, che illustra i potenziali pericoli di val mutable :

Gli oggetti mutabili possono essere modificati all’interno dei metodi, che li assumono come parametri, mentre la riassegnazione non è consentita.

 import scala.collection.mutable.ArrayBuffer object MyObject { def main(args: Array[String]) { 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}") } } 

Risultato:

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

Qualcosa di simile a questo non può accadere con var immutable , perché la riassegnazione non è consentita:

 object MyObject { def main(args: Array[String]) { 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}") } } 

Risultati in:

 error: reassignment to val 

Poiché i parametri di funzione sono trattati come val questa riassegnazione non è consentita.