Uso di def, val e var in scala


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

Queste righe di output del codice 12, sebbene siano person.age=20state eseguite con successo. Ho scoperto che questo accade perché ho usato def in def person = new Person("Kumar",12). Se uso var o val l'output è 20. Capisco che il valore predefinito è val in scala. Questo:

def age = 30
age = 45

... dà un errore di compilazione perché è un valore predefinito. Perché la prima serie di righe sopra non funziona correttamente, eppure non commette errori?

Risposte:


254

Esistono tre modi per definire le cose in Scala:

  • defdefinisce un metodo
  • valdefinisce un valore fisso (che non può essere modificato)
  • vardefinisce una variabile (che può essere modificata)

Guardando il tuo codice:

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

Questo definisce un nuovo metodo chiamato person. È possibile chiamare questo metodo solo senza ()perché è definito come metodo senza parametri. Per il metodo empty-paren, puoi chiamarlo con o senza '()'. Se scrivi semplicemente:

person

quindi stai chiamando questo metodo (e se non assegni il valore restituito, verrà semplicemente scartato). In questa riga di codice:

person.age = 20

ciò che accade è che prima si chiama il personmetodo e sul valore restituito (un'istanza di classe Person) si sta modificando la agevariabile membro.

E l'ultima riga:

println(person.age)

Qui stai di nuovo chiamando il personmetodo, che restituisce una nuova istanza di classe Person(con ageset su 12). È lo stesso di questo:

println(person().age)

27
Per confondere le cose, lo stato interno di a valpuò essere modificato ma l'oggetto a cui fa riferimento una val non può essere modificato. A valnon è una costante.
— pferrel

5
Per confondere ulteriormente le cose, val (e forse anche var, non l'ho provato) può essere usato per definire una funzione. Quando si utilizza def per definire una funzione / metodo, il corpo del def viene valutato ogni volta che viene chiamato. Quando si utilizza val, viene valutato solo nel punto di definizione. Vedere stackoverflow.com/questions/18887264/...
— Melston

1
@melston Sì, ma anche un metodo e una funzione non sono esattamente la stessa cosa .
— Jesper,

3
per confondere ulteriormente le cose, def può anche essere usato per definire le variabili membro di una classe, non necessariamente per usare var.
— Peiti Li,

2
@pferrel non è davvero confuso. Come per la finale di Java. È possibile contrassegnare un Listas final, ma è possibile modificarne il contenuto.
— jFrenetic

100

Comincerei dalla distinzione che esiste in Scala tra def , val e var .

  • def - definisce un'etichetta immutabile per il contenuto del lato destro che viene valutato pigramente - valuta per nome.

  • val - definisce un'etichetta immutabile per il contenuto del lato destro che viene valutata con entusiasmo / immediato - valutata in base al valore.

  • var : definisce una variabile mutabile , inizialmente impostata sul contenuto del lato destro valutato.

Esempio, def

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

Esempio, val

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

Esempio, var

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

scala> aVariable = 5
aVariable: Int = 5

In base a quanto sopra, le etichette di def e val non possono essere riassegnate e in caso di tentativi verrà generato un errore come quello sotto:

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

Quando la classe è definita come:

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

e quindi istanziato con:

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

un'etichetta immutabile viene creata per quella specifica istanza di una persona (cioè 'Persona'). Ogni volta che il campo mutabile 'age' deve essere modificato, tale tentativo fallisce:

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

come previsto, "age" fa parte di un'etichetta non modificabile. Il modo corretto di lavorare su questo consiste nell'utilizzare una variabile mutabile, come nel seguente esempio:

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

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

come chiaro, dal riferimento alla variabile mutabile (cioè "personaB") è possibile modificare il campo mutabile della classe "età".

Vorrei ancora sottolineare il fatto che tutto deriva dalla differenza sopra indicata, che deve essere chiara in mente a qualsiasi programmatore di Scala.


Non credo che la spiegazione sopra sia corretta. Vedi le altre risposte.
— Per Mildner,

@PerMildner Puoi per favore approfondire cosa c'è di sbagliato nella risposta sopra?
— Syed Souban,

Non ricordo quale fosse la mia lamentela originale. Tuttavia, l'ultima parte della risposta, su personAet al. sembra spento. Se la modifica del agemembro funziona o meno è indipendente dal fatto che si usi def personAo meno var personB. La differenza è che nel def personAcaso in cui si stia modificando l' Personistanza restituita dalla prima valutazione di personA. Questa istanza viene modificata, ma non è ciò che viene restituito quando si valuta nuovamente personA. Invece, la seconda volta che lo fai personA.agelo fai efficacemente new Person("Tim",25).age.
— Per Mildner,

29

Con

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

stai definendo una variabile funzione / pigra che restituisce sempre una nuova istanza Person con nome "Kumar" e età 12. Questo è totalmente valido e il compilatore non ha motivo di lamentarsi. Chiamare person.age restituirà l'età di questa istanza Person appena creata, che è sempre 12.

Durante la scrittura

person.age = 45

si assegna un nuovo valore alla proprietà age nella classe Person, che è valida poiché l'età è dichiarata come var. Il compilatore si lamenterà se si tenta di riassegnare personcon un nuovo oggetto Person come

person = new Person("Steve", 13)  // Error

Sì. Questo punto può essere facilmente dimostrato chiamando il metodo hashCode sulla persona A
— Nilanjan Sarkar

26

Per fornire un'altra prospettiva, "def" in Scala significa qualcosa che verrà valutato ogni volta che viene utilizzato, mentre val è qualcosa che viene valutato immediatamente e solo una volta . Qui, l'espressione def person = new Person("Kumar",12)implica che ogni volta che usiamo "persona" avremo una new Person("Kumar",12)chiamata. Pertanto è naturale che i due "person.age" non siano correlati.

Questo è il modo in cui capisco Scala (probabilmente in un modo più "funzionale"). Non sono sicuro se

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

è davvero ciò che Scala intende significare però. Non mi piace davvero pensare in quel modo almeno ...


20

Come dice già Kintaro, person è un metodo (a causa di def) e restituisce sempre una nuova istanza Person. Come hai scoperto, funzionerebbe se cambi il metodo in var o val:

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

Un'altra possibilità sarebbe:

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

Tuttavia, person.age=20nel tuo codice è consentito, quando torni Personun'istanza dal personmetodo e su questa istanza ti è permesso di cambiare il valore di a var. Il problema è che dopo quella linea non hai più riferimenti a quell'istanza (poiché ogni chiamata a personprodurrà una nuova istanza).

Questo non è niente di speciale, avresti esattamente lo stesso comportamento in Java:

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

8

Prendiamo questo:

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

e riscriverlo con un codice equivalente

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)

Vedi, defè un metodo. Verrà eseguito ogni volta che viene chiamato e ogni volta restituirà (a) new Person("Kumar", 12). E questo non è un errore nel "compito" perché non è in realtà un compito, ma solo una chiamata al age_=metodo (fornito da var).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.