Apa perbedaan antara a var
dan val
definisi dalam Scala dan mengapa bahasa membutuhkan keduanya? Mengapa Anda memilih val
lebih dari satu var
dan sebaliknya?
Seperti yang dikatakan oleh banyak orang lain, objek yang ditugaskan untuk val
tidak dapat diganti, dan objek yang ditugaskan ke var
kaleng. Namun, objek tersebut dapat memiliki keadaan internal yang dimodifikasi. Sebagai contoh:
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.
}
}
Jadi, meskipun kita tidak bisa mengubah objek yang ditugaskan x
, kita bisa mengubah keadaan objek itu. Pada akar itu, bagaimanapun, ada a var
.
Sekarang, kekekalan adalah hal yang baik karena berbagai alasan. Pertama, jika suatu objek tidak mengubah keadaan internal, Anda tidak perlu khawatir jika ada bagian lain dari kode Anda yang mengubahnya. Sebagai contoh:
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")
Ini menjadi sangat penting dengan sistem multithreaded. Dalam sistem multithreaded, hal berikut dapat terjadi:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Jika Anda menggunakan val
secara eksklusif, dan hanya menggunakan struktur data yang tidak dapat diubah (yaitu, hindari array, semuanya masuk scala.collection.mutable
, dll.), Anda dapat yakin bahwa ini tidak akan terjadi. Yaitu, kecuali ada beberapa kode, mungkin bahkan kerangka kerja, melakukan trik refleksi - sayangnya, refleksi dapat mengubah nilai-nilai yang "tidak berubah".
Itu satu alasan, tetapi ada alasan lain untuk itu. Saat Anda menggunakan var
, Anda dapat tergoda untuk menggunakan kembali yang sama var
untuk berbagai tujuan. Ini memiliki beberapa masalah:
Sederhananya, menggunakan val
lebih aman dan mengarah ke kode yang lebih mudah dibaca.
Maka, kita bisa pergi ke arah lain. Jika val
itu lebih baik, mengapa harus var
sama sekali? Ya, beberapa bahasa memang mengambil rute itu, tetapi ada banyak situasi di mana sifat berubah-ubah meningkatkan kinerja.
Sebagai contoh, ambil yang abadi Queue
. Ketika Anda baik enqueue
atau dequeue
sesuatu di dalamnya, Anda mendapatkan Queue
objek baru . Lalu bagaimana, apakah Anda akan memproses semua item di dalamnya?
Saya akan membahasnya dengan sebuah contoh. Katakanlah Anda memiliki antrian angka, dan Anda ingin membuat angka dari mereka. Misalnya, jika saya memiliki antrian dengan 2, 1, 3, dalam urutan itu, saya ingin mendapatkan kembali nomor 213. Mari kita selesaikan dulu dengan mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Kode ini cepat dan mudah dimengerti. Kelemahan utamanya adalah bahwa antrian yang dilewati dimodifikasi oleh toNum
, jadi Anda harus membuat salinannya terlebih dahulu. Itulah jenis manajemen objek yang membuat Anda bebas dari kekekalan.
Sekarang, mari kita tutupi dengan 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)
}
Karena saya tidak dapat menggunakan kembali beberapa variabel untuk melacak saya num
, seperti pada contoh sebelumnya, saya perlu menggunakan rekursi. Dalam hal ini, ini adalah rekursi ekor, yang memiliki kinerja yang cukup bagus. Tapi itu tidak selalu terjadi: kadang-kadang tidak ada solusi rekursi ekor yang baik (mudah dibaca, sederhana).
Namun, perhatikan bahwa saya dapat menulis ulang kode itu untuk menggunakan immutable.Queue
dan var
sekaligus! Sebagai contoh:
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
}
Kode ini masih efisien, tidak memerlukan rekursi, dan Anda tidak perlu khawatir apakah Anda harus membuat salinan antrian Anda atau tidak sebelum menelepon toNum
. Secara alami, saya menghindari menggunakan kembali variabel untuk tujuan lain, dan tidak ada kode di luar fungsi ini yang melihatnya, jadi saya tidak perlu khawatir tentang nilai-nilai mereka berubah dari satu baris ke yang berikutnya - kecuali ketika saya secara eksplisit melakukannya.
Scala memilih untuk membiarkan programmer melakukan itu, jika programmer menganggapnya sebagai solusi terbaik. Bahasa lain telah memilih untuk membuat kode seperti itu sulit. Harga yang dibayar Scala (dan bahasa apa pun dengan mutabilitas luas) adalah bahwa kompiler tidak memiliki banyak kelonggaran dalam mengoptimalkan kode sebagaimana seharusnya. Jawaban Java untuk itu adalah mengoptimalkan kode berdasarkan pada profil run-time. Kita bisa terus berbicara tentang pro dan kontra untuk masing-masing pihak.
Secara pribadi, saya pikir Scala menemukan keseimbangan yang tepat, untuk saat ini. Sejauh ini tidak sempurna. Saya pikir Clojure dan Haskell memiliki gagasan yang sangat menarik yang tidak diadopsi oleh Scala, tetapi Scala memiliki kekuatannya sendiri juga. Kita akan melihat apa yang muncul di masa depan.
var qr = q
membuat salinannyaq
?q
. Itu memang membuat salinan - di stack, bukan heap - dari referensi ke objek itu. Adapun kinerja, Anda harus lebih jelas tentang apa "itu" yang Anda bicarakan.(x::xs).drop(1)
tepatnyaxs
, bukan "salinan" darixs
) tautan di sini yang dapat saya mengerti. tnx!qr
merupakan antrian yang tidak dapat diubah, setiap kali ekspresiqr.dequeue
dipanggil, ia membuatnew Queue
(lihat < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).val
bersifat final, yaitu, tidak dapat diatur. Pikirkanfinal
dalam java.sumber
val
variabel tidak berubah, tetapi objek yang dirujuk tidak harus. Menurut tautan yang diposting Stefan: "Di sini referensi nama tidak dapat diubah untuk mengarah ke Array yang berbeda, tetapi array itu sendiri dapat dimodifikasi. Dengan kata lain isi / elemen array dapat dimodifikasi." Jadi seperti bagaimana carafinal
kerjanya di Jawa.+=
pada hashmap mutabled didefinisikan sebagaival
baik-baik saja-saya percaya persis bagaimanafinal
bekerja di javaSecara sederhana:
var = var iable
val = v ariable + fin al
sumber
Perbedaannya adalah bahwa a
var
dapat ditugaskan kembali sedangkan yangval
tidak bisa. Mutabilitas, atau selain dari apa pun yang sebenarnya ditugaskan, adalah masalah sampingan:Sedangkan:
Dan karenanya:
Jika Anda sedang membangun struktur data dan semua bidangnya adalah
val
s, maka struktur data itu tidak dapat diubah, karena statusnya tidak dapat berubah.sumber
val
berarti kekal danvar
bisa berubah.Diskusi penuh.
sumber
Berpikir dalam hal C ++,
analog dengan pointer konstan ke data tidak konstan
sementara
analog dengan penunjuk tidak konstan ke data tidak konstan
Mendukung
val
lebihvar
meningkatkan immutabilitas basis kode yang dapat memfasilitasi kebenaran, konkurensi, dan pemahaman.Untuk memahami arti memiliki pointer konstan ke data non-konstan pertimbangkan cuplikan Scala berikut:
Di sini "pointer"
val m
konstan sehingga kita tidak dapat menetapkan ulang untuk menunjuk ke hal lain seperti itunamun kita memang dapat mengubah data yang tidak konstan itu sendiri yang
m
menunjuk seperti itusumber
"val berarti tidak berubah dan var berarti bisa berubah."
Mengutip, "val berarti nilai dan var berarti variabel".
Perbedaan yang terjadi menjadi sangat penting dalam komputasi (karena kedua konsep tersebut mendefinisikan esensi dari semua pemrograman itu), dan bahwa OO telah berhasil mengaburkan hampir sepenuhnya, karena dalam OO, satu-satunya aksioma adalah bahwa "semuanya adalah obyek". Dan sebagai konsekuensinya, banyak programmer saat ini cenderung tidak memahami / menghargai / mengenali, karena mereka telah dicuci otak untuk "berpikir dengan cara OO" secara eksklusif. Seringkali mengarah ke objek variabel / bisa berubah digunakan seperti di mana - mana , ketika nilai / objek berubah mungkin / akan sering lebih baik.
sumber
Anda dapat berpikir
val
sebagaifinal
dunia kunci bahasa pemrograman java atau c ++ bahasaconst
kunci dunia。sumber
Val
berarti final , tidak dapat dipindahkanPadahal,
Var
bisa dipindahkan nanti .sumber
Ini sesederhana namanya.
sumber
Nilai - nilai diketikkan sebagai konstanta penyimpanan. Setelah dibuat nilainya tidak dapat ditugaskan kembali. nilai baru dapat didefinisikan dengan kata kunci val.
misalnya. val x: Int = 5
Di sini tipe adalah opsional karena scala dapat mengambilnya dari nilai yang diberikan.
Var - variabel adalah unit penyimpanan yang diketik yang dapat diberi nilai lagi selama ruang memori dicadangkan.
misalnya. var x: Int = 5
Data yang disimpan di kedua unit penyimpanan secara otomatis dialokasikan oleh JVM setelah ini tidak lagi diperlukan.
Dalam nilai scala lebih disukai daripada variabel karena stabilitas ini membawa ke kode terutama dalam kode bersamaan dan multithreaded.
sumber
Padahal banyak yang sudah menjawab perbedaan antara Val dan var . Tetapi satu hal yang perlu diperhatikan adalah bahwa val tidak persis seperti kata kunci akhir .
Kita dapat mengubah nilai val menggunakan rekursi tetapi kita tidak pernah bisa mengubah nilai akhir. Final lebih konstan daripada Val.
Parameter metode secara default val dan pada setiap nilai panggilan sedang diubah.
sumber