Jaka jest różnica między a var
a val
definicją w Scali i dlaczego język potrzebuje obu? Dlaczego miałbyś wybrać val
ponad var
i odwrotnie?
Jak wielu innych powiedziało, obiektu przypisanego do val
puszki nie można zastąpić, a obiektu przypisanego do var
puszki. Jednak wymieniony obiekt może mieć zmodyfikowany stan wewnętrzny. Na przykład:
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.
}
}
Tak więc, chociaż nie możemy zmienić przypisanego obiektu x
, możemy zmienić stan tego obiektu. U jego podstaw leżało jednak var
.
Niezmienność jest dobra z wielu powodów. Po pierwsze, jeśli obiekt nie zmienia stanu wewnętrznego, nie musisz się martwić, że zmienia go jakaś inna część kodu. Na przykład:
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")
Staje się to szczególnie ważne w systemach wielowątkowych. W systemie wielowątkowym mogą się zdarzyć:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Jeśli używasz val
wyłącznie i używasz niezmiennych struktur danych (to znaczy unikaj tablic, wszystkiego w scala.collection.mutable
itd.), Możesz mieć pewność, że tak się nie stanie. To znaczy, chyba że jest jakiś kod, może nawet framework, wykonujący sztuczki refleksyjne - odbicie może niestety zmienić „niezmienne” wartości.
To jeden powód, ale jest inny powód. Kiedy używasz var
, możesz ulec pokusie ponownego użycia tego samego var
do wielu celów. Ma to pewne problemy:
Mówiąc prosto, używanie val
jest bezpieczniejsze i prowadzi do bardziej czytelnego kodu.
Możemy zatem pójść w innym kierunku. Jeśli tak val
jest lepiej, dlaczego var
w ogóle mają ? Cóż, niektóre języki wybrały tę drogę, ale są sytuacje, w których zmienność bardzo poprawia wydajność.
Na przykład weź niezmienny Queue
. Kiedy obaj enqueue
lub dequeue
rzeczy w nim, pojawi się nowy Queue
obiekt. Jak więc zająłby się przetwarzaniem wszystkich elementów w nim zawartych?
Przejdę przez to z przykładem. Powiedzmy, że masz kolejkę cyfr i chcesz z nich utworzyć liczbę. Na przykład, jeśli mam kolejkę z 2, 1, 3 w tej kolejności, chcę odzyskać liczbę 213. Najpierw rozwiążmy ją za pomocą mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Ten kod jest szybki i łatwy do zrozumienia. Jego główną wadą jest to, że przekazywana kolejka jest modyfikowana toNum
, dlatego należy wcześniej wykonać jej kopię. Jest to rodzaj zarządzania obiektami, od którego niezmienność cię uwalnia.
Teraz ukryjmy to w 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)
}
Ponieważ nie mogę ponownie użyć jakiejś zmiennej do śledzenia mojej num
, tak jak w poprzednim przykładzie, muszę skorzystać z rekurencji. W tym przypadku jest to rekurencja ogona, która ma całkiem niezłą wydajność. Ale nie zawsze tak jest: czasami nie ma po prostu dobrego (czytelnego, prostego) rozwiązania rekurencji ogona.
Zauważ jednak, że mogę przepisać ten kod, aby używać immutable.Queue
i var
jednocześnie! Na przykład:
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
}
Ten kod jest nadal skuteczny, nie wymaga rekurencji i nie musisz się martwić, czy musisz wykonać kopię kolejki, czy nie, zanim zadzwonisz toNum
. Oczywiście unikałem ponownego wykorzystywania zmiennych do innych celów i żaden kod poza tą funkcją ich nie widzi, więc nie muszę się martwić, że ich wartości zmieniają się z jednej linii do drugiej - z wyjątkiem przypadków, gdy jawnie to robię.
Scala zdecydował się na to, aby programista mógł to zrobić, jeśli uzna to za najlepsze rozwiązanie. Inne języki zdecydowały się utrudnić taki kod. Cena, którą płaci Scala (i każdy język o powszechnej zmienności), polega na tym, że kompilator nie ma tak dużej swobody w optymalizacji kodu, jak mógłby inaczej. Odpowiedzią Javy na to jest optymalizacja kodu w oparciu o profil wykonawczy. Moglibyśmy ciągle mówić o zaletach i wadach każdej ze stron.
Osobiście uważam, że na razie Scala osiąga właściwą równowagę. Zdecydowanie nie jest idealny. Myślę, że zarówno Clojure, jak i Haskell mają bardzo interesujące pojęcia, które nie zostały przyjęte przez Scalę, ale Scala ma również swoje zalety. Zobaczymy, co będzie w przyszłości.
var qr = q
robi kopięq
?q
. Robi kopię - na stosie, a nie na stercie - odwołania do tego obiektu. Jeśli chodzi o wydajność, będziesz musiał lepiej określić, o czym mówisz.(x::xs).drop(1)
toxs
nie jest „kopia”xs
) linku, który mogłem zrozumieć. tnx!qr
jest to kolejka niezmienna, za każdym razem, gdyqr.dequeue
wywoływane jest wyrażenie , tworzynew Queue
( a << github.com/scala/scala/blob/2.13.x/src/library/scala/collection/... ).val
jest ostateczny, to znaczy nie można go ustawić. Pomyślfinal
w java.źródło
val
zmienne są niezmienne, ale obiekty, do których się odwołują, nie muszą być. Zgodnie z linkiem opublikowanym przez Stefana: „Nie można zmienić odwołania do nazw, aby wskazywało na inną tablicę, ale samą tablicę można modyfikować. Innymi słowy, zawartość / elementy tablicy można modyfikować”. To tak, jakfinal
działa w Javie.+=
zmienioną mapę skrótów zdefiniowaną jako poval
prostu w porządku - uważam, że dokładnie takfinal
działa w javaW prostych słowach:
var = var iable
val = v ariable + fin al
źródło
Różnica polega na tym, że
var
można przypisać a, aval
nie można. Zmienność lub w inny sposób tego, co jest faktycznie przypisane, jest kwestią uboczną:Natomiast:
I stąd:
Jeśli budujesz strukturę danych, a wszystkie jej pola są
val
s, to struktura danych jest zatem niezmienna, ponieważ jej stan nie może się zmienić.źródło
val
oznacza niezmienny ivar
oznacza zmienny.Pełna dyskusja.
źródło
Myślenie w kategoriach C ++,
jest analogiczny do stałego wskaźnika do nie stałych danych
podczas
jest analogiczny do niestałego wskaźnika do niestałych danych
Faworyzowanie w
val
stosunku dovar
zwiększa niezmienność bazy kodu, co może ułatwić jej poprawność, współbieżność i zrozumiałość.Aby zrozumieć znaczenie posiadania stałego wskaźnika do nietrwałych danych, rozważ następujący fragment kodu Scala:
W tym przypadku „wskaźnik”
val m
jest stały, więc nie możemy go ponownie przypisać, aby wskazywał na coś innegomożemy jednak rzeczywiście zmienić nietrwałe dane, które to
m
wskazująźródło
„val oznacza niezmienny, a var oznacza zmienny.”
Parafrazując „val oznacza wartość, a var oznacza zmienną”.
Rozróżnienie, które okazuje się niezwykle ważne w informatyce (ponieważ te dwie koncepcje definiują samą istotę tego, o co chodzi w programowaniu), i że OO udało się niemal całkowicie zatrzeć, ponieważ w OO jedynym aksjomatem jest to, że „wszystko jest obiekt". I w konsekwencji wielu programistów obecnie nie rozumie / nie docenia / nie rozpoznaje, ponieważ zostali oni poddani praniu mózgu wyłącznie w celu „myślenia OO”. Często prowadzi to do wykorzystywania obiektów zmiennych / zmiennych jak wszędzie , kiedy wartości / niezmienne obiekty mogłyby / często byłyby lepsze.
źródło
możesz myśleć
val
jakfinal
świat programowania w języku Java lub świat kluczy w języku C ++const
。źródło
Val
oznacza, że jego ostateczny , nie może być przeniesionyNatomiast
Var
może zostać ponownie przypisany później .źródło
To tak proste, jak sama nazwa.
źródło
Val - wartości są wpisanymi stałymi pamięci. Po utworzeniu jego wartość nie może zostać ponownie przypisana. nową wartość można zdefiniować za pomocą słowa kluczowego val.
na przykład. val x: Int = 5
Tutaj typ jest opcjonalny, ponieważ scala może wywnioskować go z przypisanej wartości.
Zmienne - zmienne są wpisywanymi jednostkami pamięci, którym można ponownie przypisać wartości, o ile zarezerwowane jest miejsce w pamięci.
na przykład. var x: Int = 5
Dane przechowywane w obu jednostkach pamięci są automatycznie rozdzielane przez JVM, gdy nie są już potrzebne.
W skalach wartości są bardziej preferowane niż zmienne ze względu na stabilność, które doprowadzają do kodu, szczególnie w kodzie współbieżnym i wielowątkowym.
źródło
Chociaż wielu już odpowiedziało na różnicę między Val a var . Należy jednak zauważyć, że val nie jest dokładnie końcowym słowem kluczowym.
Możemy zmienić wartość val za pomocą rekurencji, ale nigdy nie możemy zmienić wartości końcowej. Ostateczny jest bardziej stały niż Val.
Parametry metody są domyślnie val i przy każdej zmianie wartość połączenia jest zmieniana.
źródło