2012-06-28 21 views
6
scala> class A 
defined class A 

scala> trait B 
defined trait B 

Tạo một đối tượng của lớp A cho chúng ta:Tại sao một lớp ẩn danh được tạo khi trộn vào một đặc điểm?

scala> new A 
res4: A = [email protected] 

Nhưng việc tạo ra một đối tượng của lớp A với đặc điểm B hỗn hợp trong cho chúng ta:

scala> new A with B 
res3: A with B = [email protected] 

Ở đây chúng ta có một lớp vô danh (được gợi ý bởi anon). Tại sao ?

Đây có phải là do loại A with B được coi là loại mới (và chưa được xác định bằng số nhận dạng trước đó)?

Trả lời

13

Đ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:

  1. 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.
  2. 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

+1 đặc biệt để hiển thị ví dụ! –

+1

Câu trả lời hay, cảm ơn bạn rất nhiều. –

+0

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

6

Có. Mặc dù loại của bạn vẫn là A with B, cần phải có lớp Java cơ bản triển khai cả hai giao diện. Không có gì sai với điều đó, ngoại trừ việc nếu bạn tạo các đối tượng theo cách này hàng trăm lần, có thể bạn sẽ có hàng trăm tệp lớp. Trong trường hợp này, bạn có thể muốn tạo một số class AB extends A with B dành riêng và sau đó khởi tạo new AB.

Là một lưu ý phụ, bạn sẽ thấy rằng bạn cũng không thể trực tiếp tạo các đặc điểm, ví dụ: new B sẽ không hoạt động. Bạn cũng cần tạo một lớp rõ ràng ở đây, ví dụ: new B {}, một lần nữa dẫn đến một lớp tổng hợp ('ẩn danh').

+0

Cảm ơn bạn. Điều gì sẽ là số lượng các đối tượng ẩn danh khi một lớp chuyên dụng có ý nghĩa hơn đối với hiệu suất? 10, 100, 1000 trở lên? –

+3

Nó sẽ chỉ tạo ra một lớp mới cho mỗi lần xuất hiện của điều này trong mã của bạn, không phải cho mọi trường hợp của lớp này. Vì vậy, trừ khi bạn có 'mới A với B' trong hàng trăm dòng mã, rất khó xảy ra, không có vấn đề gì. – drexin

+0

@ John - Tôi không biết. Cá nhân tôi đã có một lớp học ở 10 trường hợp rồi; có 10 điểm khác nhau trong đó 'A với B' được khởi tạo (như @drexin nói đúng). Tôi không có trường hợp như vậy rất thường xuyên. –