2013-01-05 35 views
6

Tôi có một lớp cơ sở trừu tượng (Base) có một số đặc điểm xếp chồng được xác định cho nó (StackingTrait).Scala: Trait Mixin với Lớp cơ sở trừu tượng

trait Base { 
    def foo 
} 
trait StackingTrait extends Base { 
    abstract override def foo { super.foo } 
} 

Nó sẽ rất thuận tiện để thực hiện một lớp con bằng cách sử dụng cú pháp sau, nhưng điều này không làm việc vì trình biên dịch nói rằng foo cần phải được khai báo với override và sau đó với abstract override trên biên dịch lại, đó là không hợp lệ vì Impl là một lớp học.

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

Tôi không thể nghĩ ra lý do chính đáng tại sao cú pháp này sẽ không được phép; foo được xác định hợp lý với Impl để sắp xếp thứ tự xếp chồng xảy ra trong khái niệm vẫn giữ nguyên.

Lưu ý: Tôi đã tìm ra cách giải quyết này có hiệu quả làm điều tương tự mà tôi muốn, nhưng sự cần thiết của lớp trợ giúp làm cho tôi muốn có giải pháp tốt hơn.

class ImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

Tại sao cú pháp mong muốn không biên dịch và có giải pháp thanh lịch?

Trả lời

4

Hiểu biết của tôi là mặc dù thông báo lỗi có thể gây nhầm lẫn, nhưng hành vi là chính xác. foo được khai báo là abstract override trong StackingTrait, và do đó trong bất kỳ lớp bê tông trộn StackingTrait phải có một bê tông (không được đánh dấu như abstract) thực hiện footrướcStackingTrait (liên quan đến trật tự tuyến tính). Điều này là do super đề cập đến đặc điểm ngay trước đó theo thứ tự tuyến tính, vì vậy chắc chắn cần phải triển khai cụ thể foo trước khi StackingTrait được trộn lẫn hoặc super.foo sẽ vô nghĩa.

Khi bạn làm điều này:

class Impl extends Base with StackingTrait { 
    def foo {} 
} 

trật tự tuyến tính là Base <-StackingTrait <-Impl. Điểm duy nhất trước StackingTraitBaseBase không xác định việc triển khai cụ thể foo.

Nhưng khi bạn làm điều này:

traitImplHelper extends Base { 
    def foo {} 
} 
class Impl extends ImplHelper with StackingTrait 

Trình tự tuyến tính trở thành: Base <-ImplHelper <-StackingTrait <-Impl đây ImplHelper chứa một định nghĩa cụ thể của foo, và chắc chắn là trướcStackingTrait.

Đối với những gì đáng giá, nếu bạn đã trộn ImplHelper sau StackingTrait (như trong class Impl extends StackingTrait with ImplHelper), bạn lại có cùng một vấn đề và nó sẽ không biên dịch được.

Vì vậy, giao diện này khá phù hợp với tôi. Tôi không biết cách làm cho nó biên dịch như bạn dự định.Tuy nhiên, nếu bạn quan tâm hơn đến việc viết Impl (và có thể xác định foo ngay tại đó mà không cần một lớp/đặc điểm riêng biệt), bạn có thể viết:

trait Base { 
    protected def fooImpl 
    def foo { fooImpl } 
} 
trait StackingTrait extends Base { 
    abstract override def foo { super.foo } 
} 

class Impl extends Base with StackingTrait { 
    protected def fooImpl {} 
} 

Cũng giống như trong phiên bản gốc, bạn buộc mỗi lớp cụ thể triển khai foo (ở dạng fooImpl) và lần này nó biên dịch. Nhược điểm ở đây là trong khi fooImpl không được gọi super.foo (nó không có ý nghĩa và sẽ đi vào một vòng lặp vô hạn), trình biên dịch sẽ không cảnh báo bạn về nó.

+0

Câu trả lời hay! Tôi chuẩn bị đăng câu hỏi về tình huống này. Cảm ơn! –

+0

Tôi vừa gặp sự cố này. Sự tuyến tính hóa cuối cùng là đúng về điều này nhưng tôi nhận ra mục đích thế giới thực của các đặc điểm xếp chồng là: Bạn có thể thực hiện một sửa đổi với một đặc điểm xếp chồng chỉ cho việc thực hiện cụ thể lớp trừu tượng/đặc điểm chung. I E. có một 'danh sách lớp trừu tượng' và một danh sách mở rộng của mô-đun« trait ». Bạn không thể chỉ viết 'class Foo extends List với Mod'. Đầu tiên, bạn phải có một List cụ thể, như 'class LinkedList extends List', sau đó bạn có thể viết' class Bar mở rộng LinkedList với Mod'. Hy vọng rằng có ý nghĩa. –

+0

Tôi quên thêm rằng điều này chỉ liên quan đến các đặc điểm với các phương thức 'tóm tắt trừu tượng', phải rõ ràng. Không có các phương pháp như vậy, dường như các đặc điểm có thể được trộn lẫn một cách tự do. –

Các vấn đề liên quan