webentwicklung-frage-antwort-db.com.de

Was ist der Unterschied zwischen einer Var- und Val-Definition in Scala?

Was ist der Unterschied zwischen einer var- und val-Definition in Scala und warum benötigt die Sprache beides? Warum würden Sie eine val einer var wählen und umgekehrt?

282
Derek Mahar

Wie so viele andere gesagt haben, kann das Objekt, das einer val zugewiesen ist, nicht ersetzt werden, und das Objekt, das einer var zugewiesen ist, kann. Bei diesem Objekt kann jedoch der interne Status geändert werden. Zum Beispiel:

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

Auch wenn wir das Objekt, das x zugewiesen ist, nicht ändern können, können wir den Status dieses Objekts ändern. An der Wurzel befand sich jedoch eine var.

Nun, Unveränderlichkeit ist aus vielen Gründen eine gute Sache. Erstens, wenn ein Objekt den internen Status nicht ändert, müssen Sie sich keine Sorgen machen, wenn ein anderer Teil Ihres Codes ihn ändert. Zum Beispiel:

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

Dies ist besonders wichtig bei Multithread-Systemen. In einem Multithread-System kann Folgendes passieren:

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

Wenn Sie ausschließlich val und nur unveränderliche Datenstrukturen verwenden (d. H. Arrays vermeiden, alles in scala.collection.mutable usw.), können Sie sicher sein, dass dies nicht der Fall ist. Das heißt, es sei denn, es gibt Code, vielleicht sogar ein Framework, um Reflektionstricks auszuführen - Reflektion kann leider "unveränderliche" Werte ändern.

Das ist ein Grund, aber es gibt noch einen anderen Grund. Wenn Sie var verwenden, können Sie versucht sein, dieselbe var für mehrere Zwecke wiederzuverwenden. Das hat einige Probleme:

  • Für Leute, die den Code lesen, wird es schwieriger zu wissen, welchen Wert eine Variable in einem bestimmten Teil des Codes hat.
  • Sie vergessen möglicherweise, die Variable in einem bestimmten Codepfad erneut zu initialisieren und am Ende falsche Werte im Code weiterzuleiten.

Einfach ausgedrückt ist die Verwendung von val sicherer und führt zu besser lesbarem Code.

Wir können dann in die andere Richtung gehen. Wenn val das besser ist, warum haben var überhaupt? Nun, einige Sprachen haben diesen Weg eingeschlagen, aber es gibt Situationen, in denen die Veränderlichkeit die Leistung erheblich verbessert.

Nehmen Sie zum Beispiel eine unveränderliche Queue. Wenn Sie entweder enqueue oder dequeue darin enthalten, erhalten Sie ein neues Queue-Objekt. Wie würden Sie dann alle Artikel darin bearbeiten?

Ich werde das mit einem Beispiel durchgehen. Angenommen, Sie haben eine Warteschlange mit Ziffern und möchten aus ihnen eine Zahl zusammenstellen. Wenn ich beispielsweise eine Warteschlange mit 2, 1, 3 in dieser Reihenfolge habe, möchte ich die Nummer 213 zurückholen. Zuerst lösen wir sie mit einem mutable.Queue:

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

Dieser Code ist schnell und einfach zu verstehen. Der Hauptnachteil ist, dass die übergebene Warteschlange von toNum geändert wird. Sie müssen also vorher eine Kopie davon erstellen. Diese Art der Objektverwaltung macht Sie durch die Unveränderlichkeit frei.

Lassen Sie uns jetzt zu einem immutable.Queue konvertieren:

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

Da ich einige Variablen nicht wiederverwenden kann, um meine num zu verfolgen, wie im vorherigen Beispiel, muss ich auf Rekursion zurückgreifen. In diesem Fall handelt es sich um eine Tail-Rekursion, die eine ziemlich gute Leistung zeigt. Dies ist jedoch nicht immer der Fall: Manchmal gibt es einfach keine gute (lesbare, einfache) Lösung für die Rekursion des Schwanzes.

Beachten Sie jedoch, dass ich diesen Code umschreiben kann, um gleichzeitig einen immutable.Queue und eine var zu verwenden! Zum Beispiel:

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
}

Dieser Code ist immer noch effizient, erfordert keine Rekursion und Sie müssen sich keine Sorgen machen, ob Sie vor dem Aufruf von toNum eine Kopie Ihrer Warteschlange erstellen müssen. Natürlich habe ich es vermieden, Variablen für andere Zwecke zu verwenden, und kein Code außerhalb dieser Funktion sieht sie. Daher muss ich mir keine Sorgen machen, dass sich ihre Werte von Zeile zu Zeile ändern - es sei denn, dies ist ausdrücklich der Fall.

Scala entschied sich dafür, den Programmierer dies tun zu lassen, wenn der Programmierer dies für die beste Lösung hielt. Andere Sprachen haben sich entschieden, solchen Code zu erschweren. Der Preis, den Scala (und jede Sprache mit weit verbreiteter Wandlungsfähigkeit) zahlt, ist, dass der Compiler nicht so viel Spielraum bei der Optimierung des Codes hat, wie er es sonst tun könnte. Javas Antwort darauf ist die Optimierung des Codes basierend auf dem Laufzeitprofil. Wir könnten immer wieder über Vor- und Nachteile auf jeder Seite sprechen.

Ich persönlich denke, dass Scala vorerst die richtige Balance findet. Es ist bei weitem nicht perfekt. Ich denke, dass sowohl Clojure als auch Haskell sehr interessante Vorstellungen haben, die Scala nicht übernommen hat, aber Scala hat auch eigene Stärken. Wir werden sehen, was in die Zukunft kommt.

312

val ist final, kann also nicht gesetzt werden. Denken Sie an final in Java.

57
Jackson Davis

In einfachen Worten:

var = var fähig

val = v ariable + fin al

43
Ajay Gupta

Der Unterschied ist, dass eine var erneut zugewiesen werden kann, während eine val nicht zugewiesen werden kann. Die Veränderlichkeit oder sonstiges, was tatsächlich zugewiesen wird, ist ein Nebenproblem:

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.

Wohingegen:

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

Und daher:

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

Wenn Sie eine Datenstruktur erstellen und alle Felder vals sind, ist diese Datenstruktur daher unveränderlich, da sich ihr Status nicht ändern kann.

20
oxbow_lakes

val bedeutet unveränderlich und var bedeutet veränderlich.

Vollständige Diskussion.

19
Stefan Kendall

Denken in C++, 

val x: T

ist analog zum konstanten Zeiger auf nicht konstante Daten

T* const x;

während 

var x: T 

ist analog zu nicht konstantem Zeiger auf nicht konstante Daten 

T* x;

Die Bevorzugung von val gegenüber var erhöht die Unveränderlichkeit der Codebase, was deren Korrektheit, Parallelität und Verständlichkeit erleichtern kann.

11
Mario Galic

"val bedeutet unveränderlich und var bedeutet veränderlich."

Um es zu paraphrasieren: "val bedeutet Wert und var bedeutet Variable".

Eine Unterscheidung, die im Computerbereich äußerst wichtig ist (weil diese beiden Konzepte das Wesentliche für die Programmierung definieren) und die OO fast vollständig verschwimmen lässt, da in OO das einzige Axiom ist ist das "alles ist ein Objekt". Und das hat zur Folge, dass viele Programmierer heutzutage dazu neigen, nicht zu verstehen/zu schätzen/zu erkennen, weil sie einer Gehirnwäsche unterzogen wurden, um ausschließlich nach dem OO -Prinzip zu denken. Dies führt häufig dazu, dass variable/veränderbare Objekte wie überall verwendet werden, wenn Werte/unveränderliche Objekte häufig besser wären.

8
Erwin Smout

val bedeutet unveränderlich und var bedeutet veränderlich

sie können val als Java-Programmiersprache final-Schlüsselwelt oder C++ - Sprache const-Schlüsselwelt。 denken.

6
Rollen Holt

Es ist so einfach wie es heißt. 

var bedeutet, dass es variieren kann

val bedeutet unveränderlich

1
user105003

Obwohl viele bereits auf den Unterschied zwischen Val und var ..__ geantwortet haben, ist jedoch zu beachten, dass val nicht genau wie das Schlüsselwort final ist.

Wir können den Wert von val durch Rekursion ändern, aber wir können niemals den Wert von final ändern. Final ist konstanter als Val.

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

Methodenparameter sind standardmäßig val und bei jedem Aufrufwert wird geändert. 

Werte sind typisierte Speicherkonstanten. Sobald der Wert erstellt wurde, kann er nicht erneut zugewiesen werden. Ein neuer Wert kann mit dem Schlüsselwort val definiert werden.

z.B. Wert x: Int = 5

Typ ist hier optional, da scala es aus dem zugewiesenen Wert ableiten kann.

Var-Variablen sind typisierte Speichereinheiten, denen wieder Werte zugewiesen werden können, solange Speicherplatz reserviert ist. 

z.B. var x: Int = 5

In beiden Speichereinheiten gespeicherte Daten werden automatisch von der JVM freigegeben, sobald diese nicht mehr benötigt werden.

In Scala werden Werte gegenüber Variablen vorgezogen, da diese den Code insbesondere in gleichzeitigem und Multithreading-Code mit sich bringen.

0
SunTech

Val bedeutet final, kann nicht neu zugewiesen werden

Var kann später neu zugewiesen werden.

0