scala object
¿Cuál es la diferencia entre una definición var y val en Scala? (12)
¿Cuál es la diferencia entre una definición var
y val
en Scala y por qué el lenguaje necesita ambas cosas? ¿Por qué elegirías un val
sobre un var
y viceversa?
val significa inmutable y var significa mutable
puede pensar val
como el mundo de la clave final
lenguaje de programación de Java o el mundo de la clave de constancia del lenguaje c ++
"val significa inmutable y var significa mutable".
Parafraseando, "val significa valor y var significa variable".
Una distinción que resulta extremadamente importante en la informática (porque esos dos conceptos definen la esencia misma de la programación), y que OO ha logrado difuminar casi por completo, porque en OO, el único axioma es que "todo es un objeto". Y como consecuencia, muchos programadores en estos días tienden a no entender / apreciar / reconocer, porque se les ha lavado el cerebro para "pensar a la manera OO" exclusivamente. Por lo general, los objetos variables / mutables se usan como en todas partes , cuando los objetos de valor / inmutables podrían haber sido mejor.
Aunque muchos ya han respondido la diferencia entre Val y var . Pero un punto a tener en cuenta es que val no es exactamente como la palabra clave final .
Podemos cambiar el valor de val usando la recursión, pero nunca podemos cambiar el valor de final. Final es más constante que Val.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Los parámetros del método son por defecto val y en cada valor de llamada se está cambiando.
Como muchos otros han dicho, el objeto asignado a un val
no puede ser reemplazado, y el objeto asignado a una var
puede. Sin embargo, dicho objeto puede tener su estado interno modificado. Por ejemplo:
class A(n: Int) {
var value = n
}
class B(n: Int) {
val value = new A(n)
}
object Test {
def main(args: Array[String]) {
val x = new B(5)
x = new B(6) // Doesn''t work, because I can''t replace the object created on the line above with this new one.
x.value = new A(6) // Doesn''t work, because I can''t replace the object assigned to B.value for a new one.
x.value.value = 6 // Works, because A.value can receive a new object.
}
}
Entonces, aunque no podamos cambiar el objeto asignado a x
, podríamos cambiar el estado de ese objeto. En su raíz, sin embargo, había una var
.
Ahora, la inmutabilidad es algo bueno por muchas razones. Primero, si un objeto no cambia el estado interno, no tiene que preocuparse si alguna otra parte de su código lo está cambiando. Por ejemplo:
x = new B(0)
f(x)
if (x.value.value == 0)
println("f didn''t do anything to x")
else
println("f did something to x")
Esto se vuelve particularmente importante con los sistemas multiproceso. En un sistema multiproceso, puede suceder lo siguiente:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Si usa val
exclusivamente, y solo usa estructuras de datos inmutables (es decir, evite matrices, todo en scala.collection.mutable
, etc.), puede estar seguro de que esto no sucederá. Es decir, a menos que haya algún código, tal vez incluso un marco, haciendo trucos de reflexión: la reflexión puede cambiar valores "inmutables", desafortunadamente.
Esa es una razón, pero hay otra razón para ello. Cuando usa var
, puede verse tentado a reutilizar la misma var
para múltiples propósitos. Esto tiene algunos problemas:
- Será más difícil para las personas que leen el código saber cuál es el valor de una variable en una parte determinada del código.
- Puede olvidar reinicializar la variable en alguna ruta de código y terminar pasando valores erróneos en sentido descendente en el código.
En pocas palabras, usar val
es más seguro y conduce a un código más legible.
Podemos, entonces, ir en la otra dirección. Si val
es mejor, ¿por qué tener var
? Bueno, algunos idiomas tomaron esa ruta, pero hay situaciones en las que la mutabilidad mejora mucho el rendimiento.
Por ejemplo, tomar una Queue
inmutable. Cuando dequeue
o dequeue
cosas de la enqueue
, obtienes un nuevo objeto de Queue
. ¿Cómo entonces, irías sobre el procesamiento de todos los elementos en él?
Voy a pasar por eso con un ejemplo. Digamos que tiene una cola de dígitos y desea componer un número a partir de ellos. Por ejemplo, si tengo una cola con 2, 1, 3, en ese orden, quiero recuperar el número 213. Primero mutable.Queue
con un mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Este código es rápido y fácil de entender. Su principal inconveniente es que la cola que se pasa es modificada por toNum
, por lo que tiene que hacer una copia de antemano. Ese es el tipo de gestión de objetos de la que la inmutabilidad lo libera.
Ahora, vamos a convertirlo en un immutable.Queue
.
def toNum(q: scala.collection.immutable.Queue[Int]) = {
def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
if (qr.isEmpty)
num
else {
val (digit, newQ) = qr.dequeue
recurse(newQ, num * 10 + digit)
}
}
recurse(q, 0)
}
Como no puedo reutilizar alguna variable para realizar un seguimiento de mi num
, como en el ejemplo anterior, debo recurrir a la recursión. En este caso, es una recursión de cola, que tiene un rendimiento bastante bueno. Pero ese no es siempre el caso: a veces simplemente no hay una solución de recursión de cola buena (legible, simple).
Tenga en cuenta, sin embargo, que puedo reescribir ese código para usar un immutable.Queue
y var
al mismo tiempo! Por ejemplo:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
var qr = q
var num = 0
while (!qr.isEmpty) {
val (digit, newQ) = qr.dequeue
num *= 10
num += digit
qr = newQ
}
num
}
Este código aún es eficiente, no requiere recursión y no debe preocuparse de si debe hacer una copia de su cola o no antes de llamar toNum
. Naturalmente, evité reutilizar las variables para otros fines, y ningún código fuera de esta función las ve, por lo que no tengo que preocuparme de que sus valores cambien de una línea a la siguiente, excepto cuando lo hago explícitamente.
Scala optó por dejar que el programador haga eso, si el programador considera que es la mejor solución. Otros idiomas han optado por dificultar dicho código. El precio que Scala (y cualquier lenguaje con mutabilidad generalizada) paga es que el compilador no tiene tanto margen de maniobra para optimizar el código como podría hacerlo de otra manera. La respuesta de Java a eso es optimizar el código basado en el perfil de tiempo de ejecución. Podríamos seguir y seguir sobre los pros y los contras de cada lado.
Personalmente, creo que Scala logra el equilibrio correcto, por ahora. No es perfecto, de lejos. Creo que tanto Clojure como Haskell tienen nociones muy interesantes que Scala no ha adoptado, pero Scala también tiene sus propias fortalezas. Veremos lo que viene en el futuro.
En lenguaje sencillo:
var = var iable
val = v ariable + fin al
Es tan simple como su nombre.
var significa que puede variar
val significa invariable
La diferencia es que se puede reasignar una var
mientras que una val
no puede. La mutabilidad, o lo que sea de lo que se asigne realmente, es un problema secundario:
import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.
Mientras:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
Y por lo tanto:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Si está construyendo una estructura de datos y todos sus campos son val
, entonces esa estructura de datos es por lo tanto inmutable, ya que su estado no puede cambiar.
Los valores val son constantes de almacenamiento escritas. Una vez creado su valor no puede ser re-asignado. un nuevo valor se puede definir con la palabra clave val.
p.ej. val x: Int = 5
Aquí el tipo es opcional ya que Scala puede inferirlo del valor asignado.
Las variables variables son unidades de almacenamiento escritas a las que se les pueden asignar valores nuevamente siempre que el espacio de memoria sea reservado.
p.ej. var x: Int = 5
Los datos almacenados en ambas unidades de almacenamiento son desasignados automáticamente por JVM una vez que ya no son necesarios.
En scala, los valores son preferibles a las variables debido a la estabilidad que estos aportan al código, particularmente en el código concurrente y multiproceso.
Pensando en términos de C ++,
val x: T
es análogo al puntero constante a datos no constantes
T* const x;
mientras
var x: T
es análogo a un puntero no constante a datos no constantes
T* x;
Favorecer val
sobre var
aumenta la inmutabilidad del código base, lo que puede facilitar su corrección, concurrencia y comprensibilidad.
Val
significa su final , no puede ser reasignado
Considerando que, Var
puede ser reasignado más tarde .
val
es final, es decir, no se puede establecer. Piensa final
en java.
val
significa inmutable y var
significa mutable.