2013-04-27 46 views
5

Tôi cần một cơ chế thông minh cho thành phần thành phần cho phép trộn lẫn trong các đặc điểm để khởi tạo sau thành phần được soạn. Sau đây ném một NullPointerException:Trì hoãn khởi tạo đặc điểm

class Component { 
    def addListener(pf: PartialFunction[Any, Unit]) {} 
} 

trait DynamicComponent { 
    protected def component: Component 

    component.addListener { 
    case x => 
    } 
} 

class Foo extends DynamicComponent { 
    protected val component = new Component 
} 

new Foo // -> NullPointerException 

Những điều sau đây là không lựa chọn cho tôi:

  • Sử dụng protected lazy val component; có thể sản xuất avalange trong số hàng tá vals cần phải trở nên lười biếng, một cái gì đó Tôi không muốn.
  • Đặt addListener trong một phương pháp, ví dụ: initDynamic(); bởi vì tôi sẽ hòa nhập vào nhiều đặc điểm, và tôi không muốn nhớ nửa tá các phương pháp initFoo().
  • Sử dụng DelayedInit. Điều này không làm việc với các đặc điểm, ít nhất là theo các scaladocs.

tôi có thể sống với một init() cuộc gọi duy nhất, nhưng chỉ trong các điều kiện sau đây:

  • tất cả trộn lẫn trong những đặc điểm có thể dễ dàng khai báo để được viện dẫn trong này một cuộc gọi duy nhất
  • nó là một biên dịch lỗi để quên câu lệnh init().

Trả lời

10

Bạn có thể trì hoãn việc khởi tạo đặc điểm bằng cách sử dụng định nghĩa ban đầu. (Xem phần 5.1.6 của scala language specification)

class Foo extends { 
    protected val component = new Component 
} with DynamicComponent 
+0

Ah vâng, tôi quên mất khả năng đó. Thật không may trong trường hợp sử dụng của tôi, tôi đang có thêm một sự thừa hưởng: Tôi có 'AbstractFoo mở rộng DynamicComponent' và' Foo mở rộng AbstractFoo', trong đó thành phần được cung cấp bởi 'Foo' và phương thức vẽ của nó _call into_ được cung cấp bởi' AbstractFoo'— sử dụng định nghĩa ban đầu, 'Foo' không thấy bất kỳ phương thức nào từ' AbstractFoo' nữa. Hmm .... –

0

Dưới đây là một ý tưởng (Tôi rất hạnh phúc khi đọc về các đề xuất khác):

class Component { 
    def addListener(pf: PartialFunction[Any, Unit]) { 
    println("Added") 
    } 
} 

trait DynamicComponentHost { 
    protected def component: Component with DynamicPeer 

    protected trait DynamicPeer { 
    _: Component => 
    addListener { 
     case x => 
    } 
    } 
} 

class Foo extends DynamicComponentHost { 
    protected val component = new Component with DynamicPeer 
} 

new Foo 

Vì vậy, về cơ bản tôi đang buộc các thành phần để pha trộn trong một loại mà chỉ có thể được cung cấp bởi hỗn hợp trong đặc điểm . Hợp lý? Có vẻ hơi phức tạp trong mắt tôi.

2

Nó thậm chí còn clunkier hơn giải pháp của bạn, nhưng bạn luôn có thể yêu cầu tạo một val phải được thiết lập với các phương pháp init(). Bạn thể chọn để không làm điều đó cuối cùng và nhận được một lỗi khi chạy, nhưng ít nhất bạn sẽ không quên nó hoàn toàn:

class Component { 
    def addListener(pf: PartialFunction[Any, Unit]) { 
    println("Added") 
    } 
} 

trait Dyn { 
    protected def component: Component 
    protected val initialized: Init 
    class Init private() {} 
    private object Init { def apply() = new Init() } 
    def init() = { component.addListener{ case x => }; Init() } 
} 

class Foo extends Dyn { 
    protected val component = new Component 
    protected val initialized = init() 
} 

Không gian lận !:

> class Bar extends Dyn { protected val component = new Component } 
<console>:12: error: class Bar needs to be abstract, since value 
initialized in trait Dyn of type Bar.this.Init is not defined 
     class Bar extends Dyn { protected val component = new Component } 

Ưu điểm này có là nếu bạn cần nhiều thứ được đặt trước khi bạn khởi tạo tất cả chúng hợp tác, hoặc nếu lớp Component của bạn là final để bạn không thể kết hợp bất kỳ thứ gì khác.

1

AN ý tưởng có thể sử dụng các thủ thuật được mô tả ở đây: Cake pattern: how to get all objects of type UserService provided by components

Tất cả các thành phần của bạn mà nên được khởi tạo có thể được đăng ký tại một số Seq[InitializableComponent]. Và sau đó bạn có thể khởi tạo tất cả các thành phần đã đăng ký với một foreach.

Không có thành phần nào bị quên trong Seq đó vì chúng được đăng ký tự động, nhưng bạn vẫn có thể quên gọi cho foreach ...