Apakah ada pedoman di Scala tentang kapan menggunakan val dengan koleksi yang bisa diubah versus menggunakan var dengan koleksi yang tidak bisa diubah? Atau haruskah Anda benar-benar mengincar val dengan koleksi yang tidak dapat diubah?
Fakta bahwa ada kedua jenis koleksi itu memberi saya banyak pilihan, dan seringkali saya tidak tahu bagaimana cara menentukan pilihan itu.
scala
collections
functional-programming
immutability
James McCabe
sumber
sumber
Jawaban:
Pertanyaan yang cukup umum, yang ini. Hal yang sulit adalah menemukan duplikatnya.
Anda harus berusaha untuk transparansi referensial . Artinya, jika saya memiliki ekspresi "e", saya bisa membuat
val x = e
, dan menggantinyae
denganx
. Ini adalah properti yang dapat dirusak oleh mutabilitas. Kapan pun Anda perlu membuat keputusan desain, maksimalkan untuk transparansi referensial.Secara praktis, metode-lokal
var
adalah yang paling amanvar
yang ada, karena tidak luput dari metode tersebut. Jika metodenya pendek, bahkan lebih baik. Jika tidak, coba kurangi dengan mengekstrak metode lain.Di sisi lain, koleksi yang bisa berubah berpotensi untuk melarikan diri, meskipun tidak. Saat mengubah kode, Anda mungkin ingin meneruskannya ke metode lain, atau mengembalikannya. Itu adalah jenis hal yang merusak transparansi referensial.
Pada suatu objek (bidang), hal yang hampir sama terjadi, tetapi dengan konsekuensi yang lebih mengerikan. Bagaimanapun, objek akan memiliki status dan, oleh karena itu, merusak transparansi referensial. Tetapi memiliki koleksi yang bisa berubah berarti bahkan objek itu sendiri mungkin kehilangan kendali atas siapa yang mengubahnya.
sumber
immutable val
lebihimmutable var
lebihmutable val
lebihmutable var
. Terutamaimmutable var
selesaimutable val
!var
. Fitur bagus lainnya dalam menggunakan koleksi yang tidak dapat diubah adalah Anda dapat menyimpan salinan lama dengan efisien, meskipunvar
bermutasi.var x: Set[Int]
lebihval x: mutable.Set[Int]
karena jika Anda melewatkanx
beberapa fungsi lain, dalam kasus mantan, Anda yakin, fungsi yang tidak bisa bermutasix
untuk Anda.Jika Anda bekerja dengan koleksi yang tidak dapat diubah dan Anda perlu "memodifikasinya", misalnya, menambahkan elemen ke dalamnya dalam satu putaran, Anda harus menggunakan
var
s karena Anda perlu menyimpan koleksi yang dihasilkan di suatu tempat. Jika Anda hanya membaca dari koleksi yang tidak dapat diubah, gunakanval
s.Secara umum, pastikan Anda tidak membingungkan referensi dan objek.
val
s adalah referensi tetap (penunjuk konstan di C). Artinya, saat Anda menggunakanval x = new MutableFoo()
, Anda akan dapat mengubah objek yangx
dituju, tetapi Anda tidak akan dapat mengubah ke objek manax
. Kebalikannya berlaku jika Anda menggunakanvar x = new ImmutableFoo()
. Mengambil saran awal saya: jika Anda tidak perlu mengubah ke objek mana yang menjadi titik referensi, gunakanval
s.sumber
var immutable = something(); immutable = immutable.update(x)
mengalahkan tujuan menggunakan koleksi yang tidak dapat diubah. Anda sudah melepaskan transparansi referensial dan biasanya Anda bisa mendapatkan efek yang sama dari koleksi yang bisa berubah dengan kerumitan waktu yang lebih baik. Dari empat kemungkinan (val
danvar
, bisa berubah dan tidak bisa diubah), yang satu ini paling tidak masuk akal. Saya sering menggunakanval mutable
.var list: List[X] = Nil; list = item :: list; ...
dan saya lupa bahwa saya pernah menulis secara berbeda.Cara terbaik untuk menjawabnya adalah dengan sebuah contoh. Misalkan kita memiliki beberapa proses yang hanya mengumpulkan angka karena suatu alasan. Kami ingin mencatat nomor ini, dan akan mengirim koleksi ke proses lain untuk melakukannya.
Tentu saja, kami masih mengumpulkan nomor setelah kami mengirimkan koleksi tersebut ke logger. Dan katakanlah ada beberapa overhead dalam proses logging yang menunda logging sebenarnya. Semoga Anda bisa melihat ke mana arahnya.
Jika kita menyimpan koleksi ini dalam bentuk yang bisa berubah
val
, (bisa berubah karena kita terus menambahkannya), ini berarti bahwa proses yang melakukan pencatatan akan melihat objek yang sama yang masih diperbarui oleh proses pengumpulan kita. Koleksi itu dapat diperbarui kapan saja, jadi ketika waktunya untuk masuk, kami mungkin tidak benar-benar mencatat koleksi yang kami kirim.Jika kami menggunakan yang tidak dapat diubah
var
, kami mengirimkan struktur data yang tidak dapat diubah ke pencatat. Ketika kita menambahkan angka lebih untuk koleksi kami, kami akan mengganti kitavar
dengan struktur data berubah baru . Ini tidak berarti koleksi yang dikirim ke logger diganti! Itu masih merujuk pada koleksi yang dikirimnya. Jadi logger kami memang akan mencatat koleksi yang diterimanya.sumber
Saya pikir contoh dalam posting blog ini akan menjelaskan lebih banyak, karena pertanyaan tentang kombo mana yang akan digunakan menjadi lebih penting dalam skenario konkurensi: pentingnya kekekalan untuk konkurensi . Dan sementara kita melakukannya, perhatikan penggunaan yang lebih disukai dari sinkronisasi vs @volatile vs sesuatu seperti AtomicReference: tiga alat
sumber
var immutable
vs.val mutable
Selain banyak jawaban bagus untuk pertanyaan ini. Berikut adalah contoh sederhana, yang menggambarkan potensi bahaya
val mutable
:Objek yang dapat berubah dapat dimodifikasi di dalam metode, yang menjadikannya sebagai parameter, sementara penugasan ulang tidak diperbolehkan.
Hasil:
Sesuatu seperti ini tidak dapat terjadi dengan
var immutable
, karena penugasan ulang tidak diperbolehkan:Hasil dalam:
Karena parameter fungsi diperlakukan seperti
val
penugasan ulang ini tidak diperbolehkan.sumber
mutable val
tidak dimungkinkan denganimmutable var
. Apa di sini yang salah?+=
metode seperti buffer array. Jawaban Anda menyiratkan bahwa+=
itu sama denganx = x + y
yang sebenarnya tidak. Pernyataan Anda bahwa parameter fungsi diperlakukan sebagai vals sudah benar dan Anda mendapatkan kesalahan yang Anda sebutkan tetapi hanya karena Anda menggunakannya=
. Anda bisa mendapatkan kesalahan yang sama dengan ArrayBuffer sehingga mutabilitas koleksi di sini tidak terlalu relevan. Jadi ini bukan jawaban yang bagus karena tidak mengerti apa yang dibicarakan OP. Meskipun ini adalah contoh yang baik tentang bahaya melewatkan koleksi yang bisa berubah jika Anda tidak berniat melakukannya.ArrayBuffer
, dengan menggunakanVector
. Pertanyaan OP luas, tetapi mereka sedang mencari saran kapan harus menggunakan yang mana, jadi saya yakin jawaban saya berguna karena menggambarkan bahaya menyebarkan koleksi yang bisa berubah (fakta ituval
tidak membantu);immutable var
lebih aman darimutable val
.