Hãy bắt đầu với sự phụ thuộc theo chu kỳ.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
Tuy nhiên, tính mô-đun của giải pháp này không tuyệt vời như lần đầu tiên xuất hiện, bởi vì bạn có thể ghi đè các kiểu tự như vậy:
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
Mặc dù, nếu bạn ghi đè một thành viên của loại tự, bạn sẽ mất quyền truy cập vào thành viên ban đầu, vẫn có thể được truy cập thông qua siêu sử dụng kế thừa. Vì vậy, những gì thực sự đạt được khi sử dụng thừa kế là:
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
Bây giờ tôi không thể yêu cầu hiểu tất cả sự tinh tế của mẫu bánh, nhưng điều gây ấn tượng với tôi là phương pháp chính để thực thi mô đun là thông qua thành phần chứ không phải kiểu thừa kế hoặc kiểu tự.
Phiên bản thừa kế ngắn hơn, nhưng lý do chính tôi thích kế thừa hơn các kiểu tự là tôi thấy khó khăn hơn nhiều để có được thứ tự khởi tạo chính xác với các kiểu tự. Tuy nhiên, có một số điều bạn có thể làm với kiểu tự mà bạn không thể làm với thừa kế. Các kiểu tự có thể sử dụng một kiểu trong khi thừa kế yêu cầu một đặc điểm hoặc một lớp như trong:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
Bạn thậm chí có thể làm:
trait TypeBuster
{ this: Int with String => }
Mặc dù bạn sẽ không bao giờ có thể khởi tạo nó. Tôi không thấy bất kỳ lý do tuyệt đối nào cho việc không thể kế thừa từ một loại, nhưng tôi chắc chắn cảm thấy sẽ hữu ích khi có các lớp và đặc điểm của hàm tạo đường dẫn khi chúng ta có các đặc điểm / lớp của hàm tạo. Thật không may
trait InnerA extends Outer#Inner //Doesn't compile
Chúng tôi có thứ này:
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 }
Hoặc này:
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 }
Một điểm cần được đồng cảm nhiều hơn là các đặc điểm có thể mở rộng các lớp. Cảm ơn David Maclver đã chỉ ra điều này. Đây là một ví dụ từ mã của riêng tôi:
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
kế thừa từ lớp Swing Frame, do đó, nó có thể được sử dụng như một kiểu tự và sau đó trộn vào cuối (tại thời điểm khởi tạo). Tuy nhiên, val geomR
cần phải được khởi tạo trước khi nó được sử dụng bằng cách kế thừa các đặc điểm. Vì vậy, chúng ta cần một lớp để thực thi khởi tạo trước geomR
. Lớp ScnVista
sau đó có thể được kế thừa từ nhiều đặc điểm trực giao mà bản thân chúng có thể được thừa hưởng từ đó. Sử dụng nhiều tham số loại (generic) cung cấp một hình thức mô đun thay thế.