¿Cuál es la diferencia entre una var
y val
definición en la Scala y ¿por qué la necesidad lenguaje tanto? ¿Por qué elegirías un val
sobre var
ay viceversa?
Como muchos otros han dicho, el objeto asignado a a val
no se puede reemplazar, y el objeto asignado a una var
lata. 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 podemos cambiar el objeto asignado x
, podemos cambiar el estado de ese objeto. En la raíz de esto, sin embargo, había un var
.
Ahora, la inmutabilidad es algo bueno por muchas razones. Primero, si un objeto no cambia su 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, evitar matrices, todo lo que contiene 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, que haga trucos de reflexión; desafortunadamente, la reflexión puede cambiar valores "inmutables".
Esa es una razón, pero hay otra razón para ello. Cuando lo usa var
, puede verse tentado a reutilizar el mismo var
para múltiples propósitos. Esto tiene algunos problemas:
En pocas palabras, el uso val
es más seguro y conduce a un código más legible.
Podemos, entonces, ir en la otra dirección. Si val
eso es mejor, ¿por qué tenerlo var
? Bueno, algunos idiomas tomaron esa ruta, pero hay situaciones en las que la mutabilidad mejora mucho el rendimiento.
Por ejemplo, tome un inmutable Queue
. Cuando tú enqueue
o las dequeue
cosas en él, obtienes un nuevo Queue
objeto. Entonces, ¿cómo procesarías todos los artículos que contiene?
Voy a pasar por eso con un ejemplo. Digamos que tiene una cola de dígitos y desea componer un número de ellos. Por ejemplo, si tengo una cola con 2, 1, 3, en ese orden, quiero recuperar el número 213. Primero resolvamos con 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 debe hacer una copia de antemano. Ese es el tipo de gestión de objetos de la que la inmutabilidad te 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)
}
Debido a que no puedo reutilizar alguna variable para realizar un seguimiento de mi num
, como en el ejemplo anterior, necesito recurrir a la recursividad. 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 buena solución de recursión de cola (legible, simple).
Tenga en cuenta, sin embargo, que puedo reescribir ese código para usar una immutable.Queue
y una 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 sigue siendo eficiente, no requiere recursividad y no necesita preocuparse si tiene que hacer una copia de su cola o no antes de llamar toNum
. Naturalmente, evité reutilizar variables para otros fines, y ningún código fuera de esta función las ve, así que no necesito 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 hiciera eso, si el programador consideraba que era la mejor solución. Otros idiomas han optado por dificultar dicho código. El precio que paga Scala (y cualquier lenguaje con mutabilidad generalizada) es que el compilador no tiene tanta libertad para optimizar el código como lo haría de otra manera. La respuesta de Java a eso es optimizar el código en función del perfil de tiempo de ejecución. Podríamos seguir y seguir acerca de 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 adoptó, pero Scala también tiene sus propias fortalezas. Veremos lo que viene en el futuro.
var qr = q
una copia deq
?q
. Realiza una copia, en la pila, no en el montón, de la referencia a ese objeto. En cuanto al rendimiento, tendrá que ser más claro sobre de qué "habla".(x::xs).drop(1)
es exactamentexs
, no una "copia" dexs
) aquí podría entender el enlace . tnx!qr
es una cola inmutable, cada vez queqr.dequeue
se llama a la expresión , hace unnew Queue
(vea < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).val
es final, es decir, no se puede configurar. Piensafinal
en Java.fuente
val
variables son inmutables, pero los objetos a los que hacen referencia no tienen que serlo. Según el enlace que Stefan publicó: "Aquí la referencia de nombres no se puede cambiar para apuntar a una matriz diferente, pero la matriz en sí se puede modificar. En otras palabras, los contenidos / elementos de la matriz se pueden modificar". Entonces es comofinal
funciona en Java.+=
a un hashmap mutabled definido comoval
muy bien: creo que es exactamente cómofinal
funciona en JavaEn lenguaje sencillo:
var = var iable
val = v ariable + fin al
fuente
La diferencia es que a
var
se puede reasignar a mientras que aval
no se puede. La mutabilidad, o lo que sea de lo que realmente se asigna, es un problema secundario:Mientras:
Y por lo tanto:
Si está creando una estructura de datos y todos sus campos son
val
s, entonces esa estructura de datos es inmutable, ya que su estado no puede cambiar.fuente
val
significa inmutable yvar
significa mutable.Discusión completa
fuente
Pensando en términos de C ++,
es análogo al puntero constante a datos no constantes
mientras
es análogo al puntero no constante a datos no constantes
Favoreciendo
val
sobrevar
aumenta inmutabilidad de la base de código que puede facilitar su corrección, concurrencia y comprensibilidad.Para comprender el significado de tener un puntero constante a datos no constantes, considere el siguiente fragmento de Scala:
Aquí el "puntero"
val m
es constante, por lo que no podemos reasignarlo para que apunte a algo más asísin embargo, podemos cambiar los datos no constantes en sí mismos que
m
apuntan a gustarfuente
"val significa inmutable y var significa mutable".
Parafraseando, "val significa valor y var significa variable".
Una distinción que resulta ser extremadamente importante en 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 eso como consecuencia, muchos programadores en estos días tienden a no entender / apreciar / reconocer, porque se les ha lavado el cerebro para que "piensen de la manera OO" exclusivamente. A menudo, los objetos variables / mutables se usan como en todas partes , cuando los objetos de valor / inmutables podrían / habrían sido mejores.
fuente
puedes pensar
val
como Javafinal
Key Language Language World o C ++ Languageconst
Key World。fuente
Val
significa su final , no se puede reasignarMientras que,
Var
puede ser reasignado más tarde .fuente
Es tan simple como su nombre.
fuente
Val: los valores son constantes de almacenamiento escritas. Una vez creado, su valor no puede reasignarse. Se puede definir un nuevo valor con la palabra clave val.
p.ej. val x: Int = 5
Aquí el tipo es opcional ya que scala puede inferirlo del valor asignado.
Var: las variables son unidades de almacenamiento tipificadas a las que se les puede asignar valores nuevamente siempre que se reserve espacio en la memoria.
p.ej. var x: Int = 5
JVM desasigna automáticamente los datos almacenados en ambas unidades de almacenamiento una vez que ya no son necesarios.
En escala, los valores son preferibles a las variables debido a la estabilidad, esto trae al código particularmente en código concurrente y multiproceso.
fuente
Aunque muchos ya han respondido la diferencia entre Val y var . Pero un punto a notar es que val no es exactamente como final palabra clave .
Podemos cambiar el valor de val usando recursividad pero nunca podemos cambiar el valor de final. Final es más constante que Val.
Los parámetros del método son por defecto val y en cada valor de llamada se cambia.
fuente