Verwendung von def, val und var in scala

158
class Person(val name:String,var age:Int )
def person = new Person("Kumar",12)
person.age = 20
println(person.age)

Diese Codezeilen werden ausgegeben 12, obwohl sie person.age=20erfolgreich ausgeführt wurden. Ich habe festgestellt, dass dies passiert, weil ich def in verwendet habe def person = new Person("Kumar",12). Wenn ich var oder val benutze, ist die Ausgabe 20. Ich verstehe, die Standardeinstellung ist val in scala. Dies:

def age = 30
age = 45

... gibt einen Kompilierungsfehler aus, da es sich standardmäßig um einen Wert handelt. Warum funktionieren die ersten Zeilen oben nicht richtig und machen auch keine Fehler?

Byju Veedu
quelle

Antworten:

254

Es gibt drei Möglichkeiten, Dinge in Scala zu definieren:

  • defdefiniert eine Methode
  • valdefiniert einen festen Wert (der nicht geändert werden kann)
  • vardefiniert eine Variable (die geändert werden kann)

Betrachten Sie Ihren Code:

def person = new Person("Kumar",12)

Dies definiert eine neue Methode namens person. Sie können diese Methode nur ohne aufrufen, ()da sie als parameterlose Methode definiert ist. Für die Leer-Paren-Methode können Sie sie mit oder ohne '()' aufrufen. Wenn Sie einfach schreiben:

person

Dann rufen Sie diese Methode auf (und wenn Sie den Rückgabewert nicht zuweisen, wird er einfach verworfen). In dieser Codezeile:

person.age = 20

Was passiert ist, dass Sie zuerst die personMethode aufrufen und auf dem Rückgabewert (einer Instanz der Klasse Person) die ageMitgliedsvariable ändern .

Und die letzte Zeile:

println(person.age)

Hier rufen Sie erneut die personMethode auf, die eine neue Instanz der Klasse zurückgibt Person(mit der ageEinstellung 12). Es ist das gleiche wie das:

println(person().age)
Jesper
quelle
27
Um die Dinge zu verwirren, kann der interne Zustand von a valgeändert werden, das Objekt, auf das sich ein Wert bezieht, jedoch nicht. A valist keine Konstante.
Pferrel
5
Um die Dinge weiter zu verwirren, kann val (und vielleicht auch var, ich habe es nicht ausprobiert) verwendet werden, um eine Funktion zu definieren. Wenn Sie def verwenden, um eine Funktion / Methode zu definieren, wird der Hauptteil des Def bei jedem Aufruf ausgewertet. Bei Verwendung von val wird es nur am Definitionspunkt ausgewertet. Siehe stackoverflow.com/questions/18887264/…
Melston
1
@melston Ja, aber eine Methode und eine Funktion sind auch nicht genau dasselbe .
Jesper
3
Um die Dinge noch weiter zu verwirren, kann def auch verwendet werden, um Mitgliedsvariablen einer Klasse zu definieren, nicht unbedingt um var zu verwenden.
Peiti Li
2
@pferrel nicht wirklich verwirrend. Gleich wie bei Javas Finale. Sie können ein Listals markieren final, aber seinen Inhalt ändern.
jFrenetic
100

Ich würde mit der Unterscheidung beginnen, die in Scala zwischen def , val und var besteht .

  • def - definiert eine unveränderliche Bezeichnung für den Inhalt der rechten Seite, die träge ausgewertet wird - nach Namen auswerten.

  • val - definiert ein unveränderliches Label für den Inhalt der rechten Seite, das eifrig / sofort bewertet wird - bewertet nach Wert.

  • var - definiert eine veränderbare Variable , die anfänglich auf den ausgewerteten Inhalt der rechten Seite gesetzt wird.

Beispiel def

scala> def something = 2 + 3 * 4 
something: Int
scala> something  // now it's evaluated, lazily upon usage
res30: Int = 14

Beispiel, val

scala> val somethingelse = 2 + 3 * 5 // it's evaluated, eagerly upon definition
somethingelse: Int = 17

Beispiel var

scala> var aVariable = 2 * 3
aVariable: Int = 6

scala> aVariable = 5
aVariable: Int = 5

Gemäß den obigen Angaben können Beschriftungen von def und val nicht neu zugewiesen werden, und im Falle eines Versuchs wird ein Fehler wie der folgende ausgelöst:

scala> something = 5 * 6
<console>:8: error: value something_= is not a member of object $iw
       something = 5 * 6
       ^

Wenn die Klasse wie folgt definiert ist:

scala> class Person(val name: String, var age: Int)
defined class Person

und dann instanziiert mit:

scala> def personA = new Person("Tim", 25)
personA: Person

Für diese bestimmte Instanz von Person (dh 'personA') wird eine unveränderliche Bezeichnung erstellt. Immer wenn das veränderbare Feld "Alter" geändert werden muss, schlägt ein solcher Versuch fehl:

scala> personA.age = 44
personA.age: Int = 25

Wie erwartet ist 'Alter' Teil eines nicht veränderlichen Labels. Die richtige Vorgehensweise besteht darin, eine veränderbare Variable zu verwenden, wie im folgenden Beispiel:

scala> var personB = new Person("Matt", 36)
personB: Person = Person@59cd11fe

scala> personB.age = 44
personB.age: Int = 44    // value re-assigned, as expected

wie aus der Referenz der veränderlichen Variablen hervorgeht (dh 'personB') geht hervor, dass das klassenveränderliche Feld 'age' geändert werden kann.

Ich möchte immer noch betonen, dass alles von dem oben genannten Unterschied herrührt, der für jeden Scala-Programmierer klar sein muss.

Paolo Maresca
quelle
Ich denke nicht, dass die obige Erklärung richtig ist. Siehe die anderen Antworten.
Per Mildner
@PerMildner Können Sie bitte näher erläutern, was in der obigen Antwort falsch ist?
Syed Souban
Ich erinnere mich nicht an meine ursprüngliche Beschwerde. Der letzte Teil der Antwort über personAet al. scheint aus. Ob das Ändern des ageMitglieds funktioniert oder nicht, hängt davon ab, ob Sie def personAoder verwenden var personB. Der Unterschied besteht darin, dass Sie in dem def personAFall die Personvon Ihrer ersten Auswertung von zurückgegebene -instance ändern personA. Diese Instanz wird geändert, aber es ist nicht das, was zurückgegeben wird , wenn Sie noch einmal auszuwerten personA. Stattdessen tun personA.ageSie dies beim zweiten Mal effektiv new Person("Tim",25).age.
Per Mildner
29

Mit

def person = new Person("Kumar", 12) 

Sie definieren eine Funktion / Lazy-Variable, die immer eine neue Personeninstanz mit dem Namen "Kumar" und dem 12. Lebensjahr zurückgibt. Dies ist völlig gültig und der Compiler hat keinen Grund, sich zu beschweren. Wenn Sie person.age aufrufen, wird das Alter dieser neu erstellten Personeninstanz zurückgegeben, das immer 12 Jahre beträgt.

Beim Schreiben

person.age = 45

Sie weisen der Eigenschaft age in der Klasse Person einen neuen Wert zu, der gültig ist, da age als deklariert ist var. Der Compiler wird sich beschweren, wenn Sie versuchen, personein neues Personenobjekt wie neu zuzuweisen

person = new Person("Steve", 13)  // Error
Kintaro
quelle
Ja. Dieser Punkt kann leicht demonstriert werden, indem die hashCode-Methode für die Person A
Nilanjan Sarkar
26

Um eine andere Perspektive zu bieten, bedeutet "def" in Scala etwas, das jedes Mal ausgewertet wird, wenn es verwendet wird, während val etwas ist, das sofort und nur einmal ausgewertet wird . Hier bedeutet der Ausdruck def person = new Person("Kumar",12), dass wir immer dann einen new Person("Kumar",12)Anruf erhalten , wenn wir "Person" verwenden . Daher ist es natürlich, dass die beiden "person.age" nicht miteinander verwandt sind.

So verstehe ich Scala (wahrscheinlich "funktionaler"). Ich bin mir nicht sicher ob

def defines a method
val defines a fixed value (which cannot be modified)
var defines a variable (which can be modified)

ist wirklich das, was Scala damit meinen will. Ich denke zumindest nicht so gerne ...

xji
quelle
20

Wie Kintaro bereits sagt, ist person eine Methode (wegen def) und gibt immer eine neue Person-Instanz zurück. Wie Sie herausgefunden haben, würde es funktionieren, wenn Sie die Methode in var oder val ändern:

val person = new Person("Kumar",12)

Eine andere Möglichkeit wäre:

def person = new Person("Kumar",12)
val p = person
p.age=20
println(p.age)

In person.age=20Ihrem Code ist jedoch zulässig, wenn Sie eine PersonInstanz von der personMethode zurückerhalten , und in dieser Instanz dürfen Sie den Wert von a ändern var. Das Problem ist, dass Sie nach dieser Zeile keinen Verweis mehr auf diese Instanz haben (da jeder Aufruf von personeine neue Instanz erzeugt).

Dies ist nichts Besonderes, Sie würden in Java genau das gleiche Verhalten haben:

class Person{ 
   public int age; 
   private String name;
   public Person(String name; int age) {
      this.name = name;  
      this.age = age;
   }
   public String name(){ return name; }
}

public Person person() { 
  return new Person("Kumar", 12); 
}

person().age = 20;
System.out.println(person().age); //--> 12
Landei
quelle
8

Nehmen wir Folgendes:

class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
person.age=20
println(person.age)

und schreiben Sie es mit gleichwertigem Code neu

class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
(new Person("Kumar", 12)).age_=(20)
println((new Person("Kumar", 12)).age)

Siehe, defist eine Methode. Es wird jedes Mal ausgeführt, wenn es aufgerufen wird, und jedes Mal, wenn es zurückkehrt (a) new Person("Kumar", 12). Und dies ist kein Fehler in der "Zuweisung", da es sich nicht wirklich um eine Zuweisung handelt, sondern nur um einen Aufruf der age_=Methode (bereitgestellt von var).

Daniel C. Sobral
quelle