Qual é a diferença entre a var
e val
definition em Scala e por que o idioma precisa de ambos? Por que você escolheria um val
over a var
e vice-versa?
Como muitos outros disseram, o objeto atribuído a uma val
não pode ser substituído e o objeto atribuído a uma var
lata. No entanto, o referido objeto pode ter seu estado interno modificado. Por exemplo:
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.
}
}
Portanto, mesmo que não possamos alterar o objeto atribuído x
, poderíamos alterar o estado desse objeto. No fundo, no entanto, havia umvar
.
Agora, a imutabilidade é uma coisa boa por vários motivos. Primeiro, se um objeto não mudar de estado interno, você não precisa se preocupar se alguma outra parte do seu código está mudando. Por exemplo:
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")
Isso se torna particularmente importante nos sistemas multithread. Em um sistema multithread, o seguinte pode acontecer:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Se você usar val
exclusivamente e usar apenas estruturas de dados imutáveis (ou seja, evitar matrizes, tudo emscala.collection.mutable
etc.), tenha certeza de que isso não acontecerá. Ou seja, a menos que haja algum código, talvez até uma estrutura, fazendo truques de reflexão - a reflexão pode alterar valores "imutáveis", infelizmente.
Essa é uma razão, mas há outra razão para isso. Quando você usa var
, pode ser tentado a reutilizar o mesmo var
para várias finalidades. Isso tem alguns problemas:
Simplificando, o uso val
é mais seguro e leva a um código mais legível.
Podemos então ir na outra direção. Se val
isso é melhor, por que var
? Bem, algumas línguas seguiram esse caminho, mas há situações em que a mutabilidade melhora muito o desempenho.
Por exemplo, tome uma imutável Queue
. Quando você enqueue
ou algo dequeue
nele, você recebe uma novaQueue
objeto. Como, então, você processaria todos os itens nele?
Vou passar por isso com um exemplo. Digamos que você tenha uma fila de dígitos e deseja compor um número a partir deles. Por exemplo, se eu tiver uma fila com 2, 1, 3, nessa ordem, desejo recuperar o número 213. Vamos primeiro resolvê-lo com um 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 é rápido e fácil de entender. Sua principal desvantagem é que a fila que é passada é modificada toNum
, portanto, você deve fazer uma cópia dela com antecedência. Esse é o tipo de gerenciamento de objetos que a imutabilidade liberta.
Agora, vamos transformá-lo em um 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)
}
Porque não consigo reutilizar alguma variável para acompanhar minha num
, como no exemplo anterior, preciso recorrer à recursão. Nesse caso, é uma recursão de cauda, que tem um desempenho muito bom. Mas nem sempre é esse o caso: às vezes não há apenas uma solução boa (legível, simples) de recursão da cauda.
Note, no entanto, que posso reescrever esse código para usar um immutable.Queue
e um var
ao mesmo tempo! Por exemplo:
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
}
Esse código ainda é eficiente, não requer recursão e você não precisa se preocupar se precisa fazer uma cópia da sua fila ou não antes de ligar toNum
. Naturalmente, evitei reutilizar variáveis para outros fins, e nenhum código fora dessa função as vê; portanto, não preciso me preocupar com a alteração de seus valores de uma linha para a próxima - exceto quando explicitamente o faço.
A Scala optou por deixar o programador fazer isso, se o programador considerasse a melhor solução. Outros idiomas optaram por dificultar esse código. O preço que Scala (e qualquer idioma com grande mutabilidade) paga é que o compilador não tem tanta margem de manobra para otimizar o código quanto poderia. A resposta de Java para isso é otimizar o código com base no perfil de tempo de execução. Poderíamos continuar falando sobre prós e contras de cada lado.
Pessoalmente, acho que o Scala encontra o equilíbrio certo, por enquanto. Não é perfeito, de longe. Acho que Clojure e Haskell têm noções muito interessantes não adotadas por Scala, mas Scala também tem suas próprias forças. Vamos ver o que acontece no futuro.
var qr = q
uma cópiaq
?q
. Ele faz uma cópia - na pilha, não na pilha - da referência a esse objeto. Quanto ao desempenho, você terá que ser mais claro sobre o que está falando.(x::xs).drop(1)
é exatamentexs
, e não uma "cópia" dexs
) a partir daqui ligar eu poderia entender. tnx!qr
é uma fila imutável, toda vez que a expressãoqr.dequeue
é chamada, ela cria umnew Queue
(consulte < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).val
é final, ou seja, não pode ser definido. Pensefinal
em java.fonte
val
variáveis são imutáveis, mas os objetos a que se referem não precisam ser. De acordo com o link que Stefan postou: "Aqui, a referência de nomes não pode ser alterada para apontar para uma matriz diferente, mas a própria matriz pode ser modificada. Em outras palavras, o conteúdo / elementos da matriz podem ser modificados." Então é comofinal
funciona em Java.+=
em um hashmap mutabled definido como umval
bem-acredito seu exatamente comofinal
obras em javaEm termos simples:
var = var iable
val = v ariável + final al
fonte
A diferença é que a
var
pode ser reatribuído a enquanto que aval
não pode. A mutabilidade, ou não, do que é realmente atribuído, é uma questão secundária:Enquanto que:
E, portanto:
Se você estiver construindo uma estrutura de dados e todos os seus campos forem
val
s, essa estrutura de dados será imutável, pois seu estado não pode ser alterado.fonte
val
significa imutável evar
significa mutável.Discussão completa.
fonte
Pensando em termos de C ++,
é análogo ao ponteiro constante para dados não constantes
enquanto
é análogo a ponteiro não constante a dados não constantes
Favorecendo
val
maisvar
aumenta a imutabilidade da base de código, o que pode facilitar sua correção, simultaneidade e compreensibilidade.Para entender o significado de ter um ponteiro constante para dados não constantes, considere o seguinte snippet do Scala:
Aqui o "ponteiro"
val m
é constante, portanto não podemos atribuí-lo novamente para apontar para algo mais ou menos assimno entanto, podemos realmente alterar os dados não constantes em si que
m
apontam para gostarfonte
"val significa imutável e var significa mutável."
Parafraseando, "val significa valor e var significa variável".
Uma distinção que é extremamente importante na computação (porque esses dois conceitos definem a essência do que é a programação) e que o OO conseguiu desfocar quase completamente, porque no OO, o único axioma é que "tudo é um objeto". E que, como conseqüência, muitos programadores hoje em dia tendem a não entender / apreciar / reconhecer, porque foram submetidos a uma lavagem cerebral em "pensar da maneira OO" exclusivamente. Muitas vezes, levando a que objetos variáveis / mutáveis sejam usados como em qualquer lugar , quando objetos de valor / imutáveis podem / teriam sido melhores.
fonte
você pode pensar
val
como ofinal
mundo chave da linguagem de programação java ou o mundo chave da linguagem c ++const
。fonte
Val
significa sua final , não pode ser reatribuídaVisto que
Var
pode ser reatribuído mais tarde .fonte
É tão simples quanto o nome.
fonte
Val - values são constantes de armazenamento digitadas. Uma vez criado, seu valor não pode ser reatribuído. um novo valor pode ser definido com a palavra-chave val.
por exemplo. val x: Int = 5
Aqui, o tipo é opcional, pois o scala pode inferir a partir do valor atribuído.
Variáveis são unidades de armazenamento digitadas que podem receber valores novamente, desde que o espaço de memória seja reservado.
por exemplo. var x: Int = 5
Os dados armazenados nas duas unidades de armazenamento são automaticamente desalocados pela JVM, uma vez que não são mais necessários.
Em scala, os valores são preferidos às variáveis devido à estabilidade que elas trazem ao código, particularmente no código simultâneo e multithread.
fonte
Embora muitos já tenham respondido à diferença entre Val e var . Mas um ponto a ser observado é que val não é exatamente como a palavra-chave final .
Podemos alterar o valor de val usando recursão, mas nunca podemos alterar o valor de final. Final é mais constante que Val.
Os parâmetros do método são, por padrão, val e a cada valor de chamada está sendo alterado.
fonte