Mari kita mulai dengan ketergantungan siklus.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
Namun, modularitas dari solusi ini tidak sebesar yang pertama kali muncul, karena Anda dapat mengesampingkan tipe diri seperti itu:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
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:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
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:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
Anda bahkan dapat melakukannya:
trait TypeBuster
{ this: Int with String => }
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
trait InnerA extends Outer#Inner //Doesn't compile
Kami punya ini:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Atau ini:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
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:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
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 sebelumnya geomR
. Kelas ScnVista
kemudian dapat diwarisi dari beberapa sifat ortogonal yang dapat diwarisi sendiri. Menggunakan beberapa tipe parameter (generik) menawarkan bentuk modularitas alternatif.