software - Uso de def, val y var en scala
scala tutorial (6)
class Person(val name:String,var age:Int )
def person = new Person("Kumar",12)
person.age = 20
println(person.age)
Estas líneas de código person.age=20
resultado 12
, aunque person.age=20
se ejecutó con éxito. Descubrí que esto sucede porque utilicé def in def person = new Person("Kumar",12)
. Si uso var o val, la salida es 20
. Entiendo que el valor predeterminado es val en scala. Esta:
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?
Comenzaría por la distinción que existe en Scala entre def , val y var .
def : define una etiqueta inmutable para el contenido del lado derecho que se evalúa de forma diferida : evaluar por nombre.
val : define una etiqueta inmutable para el contenido del lado derecho que se evalúa con entusiasmo / inmediatamente , se evalúa por valor.
var : define una variable mutable , inicialmente establecida en el contenido evaluado del lado derecho.
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
De acuerdo con lo anterior, las etiquetas de def y val no se pueden reasignar, y en caso de cualquier intento se generará un error como el siguiente:
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
se crea una etiqueta inmutable para esa instancia específica de Persona (es decir, ''personaA''). Siempre que sea necesario modificar el campo mutable ''edad'', dicho intento falla:
scala> personA.age = 44
personA.age: Int = 25
como se esperaba, ''edad'' es parte de una etiqueta no mutable. La forma correcta de trabajar en esto consiste en usar una variable mutable, como en el siguiente ejemplo:
scala> var personB = new Person("Matt", 36)
personB: Person = Person@59cd11fe
scala> personB.age = 44
personB.age: Int = 44 // value re-assigned, as expected
como es claro, de la referencia variable variable (es decir, ''persona B'') es posible modificar el campo mutable de la clase ''edad''.
Todavía destacaría el hecho de que todo proviene de la diferencia mencionada anteriormente, eso tiene que ser claro en mente de cualquier programador de Scala.
Como Kintaro ya dice, la persona es un método (debido a def) y siempre devuelve una nueva instancia de Persona. Como descubriste, funcionaría si cambias el método a 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 tu código está permitido, ya que recuperas una instancia de person
método de person
, y en esta instancia puedes cambiar el valor de una var
. El problema es que después de esa línea ya no tienes más referencia a esa instancia (ya que cada llamada a person
producirá una nueva instancia).
Esto no es nada especial, tendrías 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
Con
def person = new Person("Kumar", 12)
está definiendo una función / variable diferida que siempre devuelve una nueva instancia de Persona con el nombre "Kumar" y edad 12. Esto es totalmente válido y el compilador no tiene motivos para quejarse. Llamar a person.age devolverá la edad de esta instancia de persona recién creada, que siempre es 12.
Cuando se escribe
person.age = 45
asigna un nuevo valor a la propiedad de edad en la clase Persona, que es válida ya que la edad se declara como var
. El compilador se quejará si intenta reasignar person
con un nuevo objeto Person como
person = new Person("Steve", 13) // Error
Hay tres formas 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 (que puede ser modificada)
Mirando tu 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 simplemente escribes:
person
entonces está llamando a este método (y si no asigna el valor de retorno, simplemente se descartará). En esta línea de código:
person.age = 20
lo que ocurre es que primero llama al método de la person
, y en el valor de retorno (una instancia de la clase Person
) está cambiando la variable del miembro de la age
.
Y la última línea:
println(person.age)
Aquí vuelve a llamar al método de person
, que devuelve una nueva instancia de la clase Person
(con la age
establecida en 12). Es lo mismo que esto:
println(person().age)
Para proporcionar otra perspectiva, "def" en Scala significa algo que se evaluará cada vez que se use, mientras que val es algo que se evalúa inmediatamente y solo una vez . Aquí, la expresión def person = new Person("Kumar",12)
implica que siempre que usemos "persona" obtendremos una new Person("Kumar",12)
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 a Scala (probablemente de una manera más "funcional"). No estoy seguro 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 que Scala intenta decir sin embargo. Realmente no me gusta pensar de esa manera al menos ...
Tomemos esto:
class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
person.age=20
println(person.age)
y reescribirlo con un 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)
Ver, def
es un método. Se ejecutará cada vez que se llame, y cada vez devolverá (a) una 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
).