Điều này không chỉ vì A with B
phải được coi là loại mới. Đối với hệ thống kiểu Scala, nó không trực tiếp quan trọng nếu có tồn tại một lớp tương ứng với A with B
. Một lớp ẩn danh được tạo bởi vì nó phải chứa các phương thức cầu cho tất cả các phương pháp trong các đặc điểm đã được trộn lẫn.
Lý do lớp ẩn danh được tạo ra là đối tượng phải triển khai tất cả các phương thức từ A
và tất cả các phương pháp từ B
. Ở cấp độ bytecode JVM, điều này sẽ đảm bảo kế thừa nhiều lớp và mô hình đa kế thừa không được hỗ trợ trên JVM.
Để mô phỏng đa kế thừa (hoặc thành phần mixin, tuy nhiên bạn muốn gọi nó), Scala làm những điều sau đây khi bạn tạo một đặc điểm:
- Nếu đặc điểm
T
không có triển khai phương pháp, nó tạo ra một giao diện xác định tất cả các phương thức trong đặc điểm.
Nếu đặc điểm T
có triển khai phương pháp, nó bổ sung tạo một lớp T$class
có phương pháp tĩnh cho từng phương pháp cụ thể trong T
. Phương thức tĩnh này có cùng nội dung với phương thức tương ứng của nó trong T
, nhưng chữ ký của nó được thay đổi để bao gồm thông số this
.Nếu T
thăm:
def foo(x: Int) = x
sau đó T$class
sẽ có:
<static> def foo($this: T, x: Int) = x
Lớp thu được bằng phần mixin của một số lớp A
và một số đặc điểm T
sau đó sẽ có một phương pháp cầu đặc biệt được tạo ra mà chuyển tiếp cuộc gọi đến phương pháp tĩnh chứa cơ thể. Bằng cách này, phần thân của phương thức này không bị trùng lặp trong mỗi lớp được trộn trong T
. Đây là lý do tại sao lớp ẩn danh phải được tạo ra - nó phải có phương thức cầu được xác định cho mỗi phương thức trong T
.
Đây là một ví dụ. Khi bạn tạo một lớp mới bằng cách tạo thành phần mixin, ví dụ: gọi new A with T
:
class A {
def bar = println("!")
}
trait T {
def foo(x: Int) = x
}
new A with T
trình biên dịch sẽ viết lại nó khoảng một cái gì đó như thế này:
class A {
def bar = println("!")
}
<interface> T {
def foo(x: Int): Int
}
class T$class {
<static> def foo($this: T, x: Int) = x
}
class $anon extends A <implements> T {
// notice that `bar` is inherited, but `foo` is not
<bridge> def foo(x: Int) = T$class.foo(this, x)
}
new $anon
ý rằng trình biên dịch thực sự có thể viết lại callsites để foo
để gọi các phương pháp tĩnh trực tiếp từ callsite, chứ không phải hơn thông qua một phương pháp cầu. Lý do tại sao nó không được thực hiện theo cách đó là bởi vì sau đó nó sẽ không hỗ trợ subtyping đa hình nữa.
+1 đặc biệt để hiển thị ví dụ! –
Câu trả lời hay, cảm ơn bạn rất nhiều. –
Lưu ý rằng việc gọi 'scalac' với đối số' -Xprint: mixin' sẽ hiển thị cấu trúc chính xác mà Scala tạo ra. '-Xhow-phase' hiển thị các pha khác có thể được sử dụng với' -Xprint: '. – outis