2010-12-14 35 views
134
class Person(val name:String,var age:Int) 
def person = new Person("Kumar",12) 
person.age = 20 
println(person.age) 

Estas líneas de salidas de código 12, a pesar de que person.age=20 se ha ejecutado correctamente. Descubrí que esto sucede porque utilicé def en def person = new Person("Kumar",12). Si uso var o val, la salida es 20. Entiendo que el valor predeterminado es val en scala. Esto:El uso de DEF, val, y var en Scala

def age = 30 
age = 45 

... da un error de compilación porque es un val por defecto. ¿Por qué el primer conjunto de líneas de arriba no funciona correctamente y, sin embargo, no genera errores?

Respuesta

29

Con

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

va a definir una variable de función/perezoso, que siempre devuelve una nueva instancia Persona con el nombre "Kumar" y los 12 años Esto es totalmente válido y el compilador no tiene ninguna razón para quejarse. Llamando person.age volverá la edad de esta persona instancia recién creada, que es siempre 12.

Al escribir

person.age = 45 

se asigna un nuevo valor a la propiedad de edad en la clase Persona, que es válida desde la edad se declara como var. El compilador se quejará si intenta reasignar person con un nuevo objeto persona como

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

Como Kintaro ya lo dice, la persona es un método (por definición) y siempre devuelve una nueva instancia persona. Como se enteró de que iba a funcionar si cambia el método a una var o val:

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

Otra posibilidad sería:

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

Sin embargo, person.age=20 en su código está permitido, como regrese una instancia Person del método person, y en esta instancia puede cambiar el valor de var. El problema es que después de esa línea ya no tendrá más referencias a esa instancia (ya que cada llamada a person producirá una nueva instancia).

Esto no es nada especial, que tendría exactamente el mismo comportamiento en 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 
+0

Esta respuesta es la explicación más clara. – stackoverflowuser2010

221

Hay tres maneras de definir las cosas en Scala:

  • def define un método
  • val define un valor fijo (que no se puede modificar)
  • var define una variable de (que puede ser modificado)

En cuanto a su código:

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

Esto define un nuevo método llamado person. Puede llamar a este método solo sin () porque está definido como método sin parámetros. Para el método empty-paren, puede llamarlo con o sin '()'. Si escribe simplemente:

person 

entonces se llama a este método (y si no se asigna el valor de retorno, sólo serán descartados). En esta línea de código:

person.age = 20 

lo que sucede es que primero llama al método person, y en el valor de retorno (una instancia de la clase Person) está cambiando la variable age miembro.

Y la última línea:

println(person.age) 

Aquí están de nuevo una llamada al método person, que devuelve una nueva instancia de la clase Person (con age conjunto a 12). Es lo mismo que esto:

println(person().age) 
+19

Para confundir las cosas, se puede cambiar el estado interno de '' 'val''' pero el objeto al que hace referencia un valor val no puede. Un '' 'val''' no es una constante. – pferrel

+3

Para confundir aún más las cosas, val (y tal vez var también, no lo he probado) se pueden usar para definir una función. Cuando se utiliza def para definir una función/método, el cuerpo de la def se evalúa cada vez que se llama. Cuando se usa val, se evalúa solo en el punto de definición. Consulte http://stackoverflow.com/questions/18887264/what-is-the-difference-between-def-and-val-to-define-a-function – melston

+1

@melston Sí, pero un * método * y una función * * tampoco son exactamente [lo mismo] (http://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala). – Jesper

8

Tomemos esto:

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

y volver a escribir con código 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) 

Sede, def es un método. Se ejecutará cada vez que se llame, y cada vez devolverá (a) new Person("Kumar", 12). Y esto no es un error en la "asignación" porque no es realmente una tarea, sino solo una llamada al método age_= (provisto por var).

23

para proporcionar otra perspectiva, "def" en la Scala significa algo que va a ser evaluado cada vez cuando se usa, mientras que val es algo que es evaluado inmediatamente y sólo una vez. Aquí, la expresión def person = new Person("Kumar",12) implica que cada vez que usemos "persona" recibiremos una llamada new Person("Kumar",12). Por lo tanto, es natural que los dos "persona.age" no estén relacionados.

Esta es la forma en que entiendo Scala (probablemente de una manera más "funcional"). No estoy seguro de si

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

es realmente lo Scala tiene la intención de significar sin embargo. Realmente no me gusta pensar de esa manera al menos ...

70

Yo empezaría por la distinción que existe en Scala entre def, val y var.

  • def - define un etiqueta inmutable por el contenido banda derecha que se perezosamente evaluaron - evaluar por su nombre.

  • val - define un etiqueta inmutable por el contenido banda derecha que es ansiosamente/inmediatamente evaluado - evaluado por valor.

  • var - define una variable mutable , inicialmente establecido en el contenido lado derecho evaluado.

ejemplo, def

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

ejemplo, val

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

ejemplo, var

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

scala> aVariable = 5 
aVariable: Int = 5 

Según anteriormente, las etiquetas de def y val no pueden ser reasignados, y en caso de cualquier intento de un error como se planteó la debajo de uno:

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

Cuando la clase se define como:

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

y luego instanciado con:

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

etiqueta inmutable se crea para esa instancia específica de Persona (es decir, 'persona'). Siempre que la 'edad' del campo mutable necesita ser modificada, tal intento falla:

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

como se esperaba, 'edad' es parte de una etiqueta no mutable.La forma correcta para trabajar en este consiste en el uso de una variable mutable, como en el siguiente ejemplo:

scala> var personB = new Person("Matt", 36) 
personB: Person = [email protected] 

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

tan claro, a partir de la mutable de referencia variable (es decir, 'PersonB') es posible modificar la clase campo mutable 'edad'.

Todavía destacaría el hecho de que todo proviene de la diferencia antes mencionada, que debe tenerse en cuenta en cualquier programador de Scala.

Cuestiones relacionadas