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.