Tipe diri untuk suatu sifat A
:
trait B
trait A { this: B => }
mengatakan bahwa " A
tidak dapat dicampur ke dalam kelas konkret yang tidak juga diperluas B
" .
Di sisi lain, berikut ini:
trait B
trait A extends B
mengatakan bahwa "setiap pencampuran kelas (konkret atau abstrak) A
juga akan bercampur dalam B" .
Bukankah kedua pernyataan ini memiliki arti yang sama? Tipe diri tampaknya hanya berfungsi untuk menciptakan kemungkinan kesalahan waktu kompilasi sederhana.
Apa yang saya lewatkan?
trait A[Self] {this: Self => }
legal,trait A[Self] extends Self
bukan.Jawaban:
Ini terutama digunakan untuk Injeksi Ketergantungan , seperti dalam Pola Kue. Ada artikel bagus yang membahas berbagai bentuk injeksi ketergantungan di Scala, termasuk Pola Kue. Jika Anda Google "Pola Kue dan Scala", Anda akan mendapatkan banyak tautan, termasuk presentasi dan video. Untuk saat ini, di sini ada tautan ke pertanyaan lain .
Sekarang, seperti apa perbedaan antara tipe diri dan memperluas sifat, itu sederhana. Jika Anda mengatakan
B extends A
, makaB
adalah sebuahA
. Ketika Anda menggunakan tipe-diri,B
dibutuhkan sebuahA
. Ada dua persyaratan khusus yang dibuat dengan tipe mandiri:B
diperpanjang, maka Anda harus mencampurA
.A
.Perhatikan contoh-contoh berikut:
Jika
Tweeter
itu adalah subkelas dariUser
, tidak akan ada kesalahan. Pada kode di atas, kita diperlukan sebuahUser
setiap kaliTweeter
digunakan, namunUser
tidak disediakan untukWrong
, jadi kita punya kesalahan. Sekarang, dengan kode di atas masih dalam cakupan, pertimbangkan:Dengan
Right
, persyaratan untuk mencampuradukkanUser
adalah terpenuhi. Namun, persyaratan kedua yang disebutkan di atas tidak puas: beban implementasiUser
masih ada untuk kelas / sifat yang meluasRight
.Dengan
RightAgain
kedua persyaratan terpenuhi. AUser
dan implementasiUser
disediakan.Untuk kasus penggunaan yang lebih praktis, silakan lihat tautan di awal jawaban ini! Tapi, semoga sekarang Anda mengerti.
sumber
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent
? Itu akan menyebabkanWarmerComponentImpl
memiliki antarmuka tersebut. Mereka akan tersedia untuk apa pun yang diperpanjangWarmerComponentImpl
, yang jelas salah, karena tidak seorangSensorDeviceComponent
, ataupunOnOffDeviceComponent
. Sebagai tipe mandiri, dependensi ini tersedia secara eksklusif untukWarmerComponentImpl
. AList
dapat digunakan sebagaiArray
, dan sebaliknya. Tapi mereka tidak sama.this
dengan tipe-tipe diri adalah sesuatu yang saya pandang rendah, karena bayangan itu tanpa alasan aslithis
.self: Dep1 with Dep2 =>
.Jenis mandiri memungkinkan Anda untuk menentukan dependensi siklus. Misalnya, Anda dapat mencapai ini:
Penggunaan warisan
extends
tidak memungkinkan hal itu. Mencoba:Dalam buku Odersky, lihat bagian 33.5 (Membuat bab UI lembar bentang) di mana ia menyebutkan:
Semoga ini membantu.
sumber
Satu perbedaan tambahan adalah bahwa tipe diri dapat menentukan tipe non-kelas. Contohnya
Tipe mandiri di sini adalah tipe struktural. Efeknya adalah untuk mengatakan bahwa apa pun yang bercampur di Foo harus menerapkan unit pengembalian metode "tutup" no-arg. Ini memungkinkan mixin aman untuk mengetik bebek.
sumber
abstract class A extends {def close:Unit}
setara denganabstract class A {def close:Unit}
. Jadi tidak melibatkan tipe struktural.Bagian 2.3 "Anotasi Selftipe" dari makalah Scalaable Scalable Component Abstraksi karya Martin Odersky sebenarnya menjelaskan tujuan selftype di luar komposisi mixin dengan sangat baik: memberikan cara alternatif untuk mengasosiasikan kelas dengan jenis abstrak.
Contoh yang diberikan di koran seperti berikut ini, dan sepertinya tidak memiliki koresponden subclass yang elegan:
sumber
Hal lain yang belum disebutkan: karena tipe-diri bukan bagian dari hierarki kelas yang diperlukan, mereka dapat dikecualikan dari pencocokan pola, terutama ketika Anda secara menyeluruh mencocokkan dengan hierarki yang disegel. Ini nyaman ketika Anda ingin memodelkan perilaku ortogonal seperti:
sumber
TL; DR ringkasan jawaban lain:
Tipe yang Anda rentangkan terkena tipe yang diwarisi, tetapi tipe diri tidak
misalnya:
class Cow { this: FourStomachs }
memungkinkan Anda menggunakan metode yang hanya tersedia untuk ruminansia, sepertidigestGrass
. Namun sifat-sifat yang memperpanjang Sapi tidak akan memiliki hak istimewa seperti itu. Di sisi lain,class Cow extends FourStomachs
akan mengeksposdigestGrass
kepada siapa saja yangextends Cow
.tipe diri memungkinkan dependensi siklus, memperluas tipe lain tidak
sumber
Mari kita mulai dengan ketergantungan siklus.
Namun, modularitas dari solusi ini tidak sebesar yang pertama kali muncul, karena Anda dapat mengesampingkan tipe diri seperti itu:
Meskipun, jika Anda menimpa anggota tipe diri, Anda kehilangan akses ke anggota asli, yang masih dapat diakses melalui super menggunakan pewarisan. Jadi yang benar-benar didapat dari menggunakan warisan adalah:
Sekarang saya tidak bisa mengklaim memahami semua seluk-beluk pola kue, tetapi menurut saya metode utama menegakkan modularitas adalah melalui komposisi daripada tipe pewarisan atau mandiri.
Versi warisan lebih pendek, tetapi alasan utama saya lebih memilih warisan daripada tipe diri adalah bahwa saya merasa jauh lebih sulit untuk mendapatkan urutan inisialisasi yang benar dengan tipe diri. Namun, ada beberapa hal yang dapat Anda lakukan dengan tipe diri yang tidak dapat Anda lakukan dengan pewarisan. Tipe mandiri dapat menggunakan tipe sementara pewarisan memerlukan sifat atau kelas seperti pada:
Anda bahkan dapat melakukannya:
Meskipun Anda tidak akan pernah bisa instantiate. Saya tidak melihat alasan mutlak untuk tidak dapat mewarisi dari suatu tipe, tetapi saya tentu merasa akan berguna untuk memiliki kelas dan sifat path konstruktor karena kami memiliki tipe / kelas konstruktor tipe. Sayangnya
Kami punya ini:
Atau ini:
Satu hal yang harus lebih ditekankan adalah bahwa sifat dapat memperluas kelas. Terima kasih kepada David Maclver karena menunjukkan ini. Berikut ini contoh dari kode saya sendiri:
ScnBase
mewarisi dari kelas Swing Frame, sehingga dapat digunakan sebagai tipe mandiri dan kemudian dicampur di akhir (di instantiation). Namun,val geomR
perlu diinisialisasi sebelum digunakan oleh pewarisan sifat. Jadi kita perlu kelas untuk menegakkan inisialisasi sebelumnyageomR
. KelasScnVista
kemudian dapat diwarisi dari beberapa sifat ortogonal yang dapat diwarisi sendiri. Menggunakan beberapa tipe parameter (generik) menawarkan bentuk modularitas alternatif.sumber
sumber
Tipe mandiri memungkinkan Anda menentukan tipe apa yang diizinkan untuk menggabungkan suatu sifat. Misalnya, jika Anda memiliki sifat dengan tipe diri
Closeable
, maka sifat itu tahu bahwa satu-satunya hal yang diizinkan untuk menggabungkannya, harus mengimplementasikanCloseable
antarmuka.sumber
trait A { self:B => ... }
maka deklarasiX with A
hanya valid jika X memanjang B. Ya, Anda bisa mengatakanX with A with Q
, di mana Q tidak memperpanjang B, tapi saya percaya poin kikibobo adalah bahwa X sangat dibatasi. Atau apakah saya melewatkan sesuatu?Pembaruan: Perbedaan utama adalah bahwa tipe-mandiri dapat bergantung pada beberapa kelas (saya akui itu adalah kasus sudut sedikit). Misalnya, Anda bisa memilikinya
Hal ini memungkinkan untuk menambahkan
Employee
mixin hanya untuk sesuatu yang merupakan subclass dariPerson
danExpense
. Tentu saja, ini hanya bermakna jikaExpense
diperluasPerson
atau sebaliknya. Intinya adalah bahwa menggunakan tipe diriEmployee
bisa independen dari hirarki kelas yang menjadi sandarannya. Tidak peduli apa yang memperpanjang apa - Jika Anda mengganti hierarkiExpense
vsPerson
, Anda tidak perlu memodifikasiEmployee
.sumber
dalam kasus pertama, suatu sub-sifat atau sub-kelas B dapat dicampur ke dalam penggunaan apa pun A. Jadi B dapat menjadi sifat abstrak.
sumber