Jawaban:
Saya dapat memikirkan dua perbedaan
Ada bagian dalam Pemrograman dalam Scala yang disebut "To trait, or not to trait?" yang membahas pertanyaan ini. Karena edisi pertama tersedia online, saya harap tidak masalah mengutip semuanya di sini. (Setiap programmer Scala yang serius harus membeli buku itu):
Setiap kali Anda menerapkan kumpulan perilaku yang dapat digunakan kembali, Anda harus memutuskan apakah Anda ingin menggunakan sifat atau kelas abstrak. Tidak ada aturan yang pasti, tetapi bagian ini berisi beberapa pedoman untuk dipertimbangkan.
Jika perilakunya tidak akan digunakan kembali , maka buatlah kelas yang konkret. Bagaimanapun juga, itu bukan perilaku yang dapat digunakan kembali.
Jika itu dapat digunakan kembali dalam beberapa, kelas yang tidak terkait , buatlah suatu sifat. Hanya sifat-sifat yang dapat dicampur ke berbagai bagian hirarki kelas.
Jika Anda ingin mewarisi darinya dalam kode Java , gunakan kelas abstrak. Karena sifat-sifat dengan kode tidak memiliki analog Java yang dekat, itu cenderung canggung untuk mewarisi dari sifat dalam kelas Java. Mewarisi dari kelas Scala, sementara itu, persis seperti mewarisi dari kelas Java. Sebagai satu pengecualian, sifat Scala dengan anggota abstrak hanya menerjemahkan langsung ke antarmuka Java, jadi Anda harus merasa bebas untuk mendefinisikan sifat-sifat tersebut bahkan jika Anda mengharapkan kode Java untuk mewarisi darinya. Lihat Bab 29 untuk informasi lebih lanjut tentang bekerja dengan Java dan Scala bersama.
Jika Anda berencana untuk mendistribusikannya dalam bentuk yang dikompilasi , dan Anda mengharapkan grup luar untuk menulis kelas yang mewarisi darinya, Anda mungkin cenderung menggunakan kelas abstrak. Masalahnya adalah ketika suatu sifat mendapatkan atau kehilangan anggota, setiap kelas yang mewarisinya harus dikompilasi ulang, bahkan jika mereka belum berubah. Jika klien luar hanya akan memanggil perilaku, alih-alih mewarisinya, maka menggunakan sifat baik-baik saja.
Jika efisiensi sangat penting , condong ke arah menggunakan kelas. Kebanyakan Java runtimes membuat doa metode virtual anggota kelas menjadi operasi yang lebih cepat daripada doa metode antarmuka. Ciri-ciri dikompilasi ke antarmuka dan karenanya dapat membayar sedikit overhead kinerja. Namun, Anda harus membuat pilihan ini hanya jika Anda tahu bahwa sifat tersebut merupakan hambatan kinerja dan memiliki bukti bahwa menggunakan kelas bukannya benar-benar menyelesaikan masalah.
Jika Anda masih belum tahu , setelah mempertimbangkan hal di atas, maka mulailah dengan menjadikannya sebagai sifat. Anda selalu dapat mengubahnya nanti, dan secara umum menggunakan suatu sifat membuat lebih banyak opsi terbuka.
Seperti @Mushtaq Ahmed sebutkan, suatu sifat tidak dapat memiliki parameter apa pun yang diteruskan ke konstruktor utama suatu kelas.
Perbedaan lain adalah perawatan super
.
Perbedaan lain antara kelas dan sifat adalah bahwa sedangkan di kelas,
super
panggilan terikat secara statis, dalam sifat, mereka terikat secara dinamis. Jika Anda menulissuper.toString
di kelas, Anda tahu persis implementasi metode mana yang akan dipanggil. Ketika Anda menulis hal yang sama dalam suatu sifat, implementasi metode untuk memanggil panggilan super tidak ditentukan saat Anda mendefinisikan sifat tersebut.
Lihat sisa Bab 12 untuk lebih jelasnya.
Edit 1 (2013):
Ada perbedaan kecil dalam perilaku kelas abstrak dibandingkan dengan sifat. Salah satu aturan linierisasi adalah bahwa ia mempertahankan hierarki warisan kelas-kelas, yang cenderung mendorong kelas-kelas abstrak belakangan dalam rantai sementara sifat-sifat dapat dengan senang hati dicampurkan. Dalam keadaan tertentu, sebenarnya lebih disukai berada pada posisi terakhir dari linierisasi kelas. , jadi kelas abstrak bisa digunakan untuk itu. Lihat membatasi linierisasi kelas (urutan mixin) di Scala .
Edit 2 (2018):
Pada Scala 2.12, perilaku kompatibilitas biner sifat telah berubah. Sebelum 2.12, menambahkan atau menghapus anggota ke sifat diperlukan kompilasi ulang semua kelas yang mewarisi sifat itu, bahkan jika kelas tidak berubah. Ini karena cara pengkodean dalam JVM.
Pada Scala 2.12, ciri mengkompilasi ke antarmuka Java , sehingga persyaratan telah sedikit santai. Jika sifat melakukan salah satu dari yang berikut, subclassnya masih memerlukan kompilasi ulang:
- mendefinisikan bidang (
val
atauvar
, tetapi konstanta ok -final val
tanpa tipe hasil)- panggilan
super
- pernyataan inisialisasi dalam tubuh
- memperluas kelas
- mengandalkan linierisasi untuk menemukan implementasi di supertrait yang tepat
Tetapi jika sifat itu tidak, Anda sekarang dapat memperbaruinya tanpa merusak kompatibilitas biner.
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine
- Bisakah seseorang menjelaskan apa bedanya di sini? extends
vs with
?
extends
dan with
. Ini murni sintaksis. Jika Anda mewarisi dari banyak templat, yang pertama didapat extend
, semua yang lain dapatkan with
, itu saja. Pikirkan with
sebagai koma: class Foo extends Bar, Baz, Qux
.
Untuk apa pun nilainya, Pemrograman Odersky et al di Scala merekomendasikan bahwa, ketika Anda ragu, Anda menggunakan sifat-sifat. Anda selalu dapat mengubahnya menjadi kelas abstrak nanti jika diperlukan.
Selain fakta bahwa Anda tidak dapat secara langsung memperluas beberapa kelas abstrak, tetapi Anda dapat menggabungkan beberapa sifat ke dalam kelas, perlu disebutkan bahwa sifat dapat ditumpuk, karena panggilan super dalam suatu sifat terikat secara dinamis (merujuk pada suatu kelas atau sifat yang dicampur sebelum saat ini).
Dari jawaban Thomas dalam Perbedaan antara Kelas Abstrak dan Sifat :
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1
Dalam Pemrograman Scala , penulis mengatakan bahwa kelas abstrak membuat hubungan objek "is-a" klasik berorientasi sedangkan sifat adalah scala-cara komposisi.
Kelas abstrak dapat berisi perilaku - Mereka dapat parameter dengan konstruktor args (yang tidak dapat ciri) dan mewakili entitas kerja. Alih-alih sifat hanya mewakili satu fitur, antarmuka dari satu fungsi.
trait Enumerable
dengan banyak fungsi pembantu, saya tidak akan menyebutnya perilaku tetapi hanya fungsionalitas yang terhubung dengan satu fitur.