A def
dapat diimplementasikan oleh a def
, a val
, a lazy val
atau an object
. Jadi ini adalah bentuk paling abstrak untuk mendefinisikan anggota. Karena sifat biasanya antarmuka abstrak, mengatakan Anda menginginkan a val
berarti mengatakan bagaimana implementasi harus dilakukan. Jika Anda meminta a val
, kelas pelaksana tidak dapat menggunakan def
.
A val
diperlukan hanya jika Anda membutuhkan pengenal yang stabil, misalnya untuk tipe yang bergantung pada jalur. Itu adalah sesuatu yang biasanya tidak Anda butuhkan.
Membandingkan:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) }
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = {
Thread.sleep(5000)
42
}
}
Jika Anda punya
trait Foo { val bar: Int }
Anda tidak akan bisa mendefinisikan F1
atau F3
.
Oke, dan untuk membingungkan Anda dan jawab @ om-nom-nom — menggunakan abstrak val
dapat menyebabkan masalah inisialisasi:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Ini adalah masalah buruk yang menurut pendapat pribadi saya harus hilang di versi Scala mendatang dengan memperbaikinya di kompiler, tapi ya, saat ini ini juga merupakan alasan mengapa seseorang tidak boleh menggunakan abstrak val
.
Sunting (Jan 2016): Anda diizinkan untuk mengganti val
deklarasi abstrak dengan lazy val
implementasi, sehingga akan mencegah kegagalan inisialisasi.
val
dengan alazy val
. Pernyataan Anda bahwa Anda tidak akan dapat membuatF3
jikabar
ituval
tidak benar. Yang mengatakan, anggota abstrak dalam sifat harus selaludef
'sval schoko = bar + bar
denganlazy val schoko = bar + bar
. Itulah salah satu cara untuk memiliki kendali atas urutan inisialisasi. Selain itu, menggunakanlazy val
alih-alihdef
di kelas turunan menghindari penghitungan ulang.val bar: Int
kedef bar: Int
Fail.schoko
masih nol.Saya lebih suka tidak menggunakan
val
sifat karena deklarasi val memiliki urutan inisialisasi yang tidak jelas dan tidak intuitif. Anda dapat menambahkan sifat ke hierarki yang sudah berfungsi dan itu akan merusak semua hal yang berfungsi sebelumnya, lihat topik saya: mengapa menggunakan val biasa di kelas non-finalAnda harus mengingat semua hal tentang menggunakan deklarasi val ini yang pada akhirnya mengarahkan Anda ke kesalahan.
Perbarui dengan contoh yang lebih rumit
Tetapi ada kalanya Anda tidak bisa menghindari penggunaan
val
. Seperti yang telah disebutkan @ 0__ terkadang Anda memerlukan pengenal yang stabil dandef
bukan pengenal .Saya akan memberikan contoh untuk menunjukkan apa yang dia bicarakan:
trait Holder { type Inner val init : Inner } class Access(val holder : Holder) { val access : holder.Inner = holder.init } trait Access2 { def holder : Holder def access : holder.Inner = holder.init }
Kode ini menghasilkan kesalahan:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found. def access : holder.Inner =
Jika Anda meluangkan waktu sejenak untuk berpikir Anda akan mengerti bahwa kompilator memiliki alasan untuk mengeluh. Dalam
Access2.access
kasus ini tidak bisa mendapatkan tipe pengembalian dengan cara apapun.def holder
artinya bisa diimplementasikan secara luas. Itu bisa mengembalikan pemegang yang berbeda untuk setiap panggilan dan pemegang itu akan memasukkanInner
jenis yang berbeda . Tetapi mesin virtual Java mengharapkan jenis yang sama dikembalikan.sumber
Selalu menggunakan def sepertinya agak canggung karena sesuatu seperti ini tidak akan berfungsi:
trait Entity { def id:Int} object Table { def create(e:Entity) = {e.id = 1 } }
Anda akan mendapatkan kesalahan berikut:
error: value id_= is not a member of Entity
sumber
var
. Intinya adalah, jika itu adalah bidang, mereka harus ditetapkan seperti itu. Saya hanya berpikir memiliki segalanya karenadef
rabun dekat.var
mari kita hancurkan enkapsulasi. Tetapi menggunakandef
(atau aval
) lebih disukai daripada variabel global. Saya pikir apa yang Anda cari adalah sesuatu seperticase class ConcreteEntity(override val id: Int) extends Entity
sehingga Anda dapat membuatnya daridef create(e: Entity) = ConcreteEntity(1)
Ini lebih aman daripada memecahkan enkapsulasi dan mengizinkan kelas apa pun untuk mengubah Entitas.