2013-04-27 10 views
5

Potrzebuję inteligentnego mechanizmu do komponowania składu, który pozwala mieszanym cechom zainicjować po komponencie komponowanej. Poniższy rzuca NullPointerException:Inicjowanie opóźnionej cechy

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 

Następujące rzeczy są opcje nie dla mnie:

  • Korzystanie protected lazy val component; to by wyprodukować avalange z kilkudziesięciu valsów, które muszą stać się leniwe, coś Nie chcę.
  • Umieszczenie addListener w metodzie, np. initDynamic(); ponieważ będę mieszał w wielu cechach i nie chcę pamiętać o wywołaniu kilku metod initFoo().
  • Używanie DelayedInit. To nie działa z cechami, przynajmniej według skaladoców.

mógłbym żyć z jednym init() rozmowy, ale tylko pod następującymi warunkami:

  • wszystkich miesza się cechami łatwo deklarować się powoływać w tej jednej rozmowy
  • jest kompilacji błąd zapomnieć o oświadczeniu init().

Odpowiedz

10

Możesz opóźnić inicjalizację cechy, korzystając z wcześniejszych definicji . (Patrz punkt 5.1.6 w scala language specification)

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

Ach tak, zapomniałem o tej możliwości. Niestety w moim przypadku użycia, mam dodatkowe pośrednie: Mam 'jak AbstractFoo rozszerza DynamicComponent' i' Foo extends AbstractFoo', gdzie składnik jest przez 'Foo' i jego metody malowania _call w_ metod dostarczonych przez' AbstractFoo'- używając wczesnej definicji, 'Foo' nie widzi żadnych metod z' AbstractFoo'. Hmm .... –

0

Oto jeden pomysł (jestem szczęśliwy, aby przeczytać o inne propozycje):

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 

Więc w zasadzie jestem zmuszając komponent mieszać w rodzaju, które mogą być zrealizowane tylko przez mieszany w cechy . Rozsądny? Wygląda trochę zbyt skomplikowanie w moich oczach.

2

To nawet clunkier niż rozwiązania, ale zawsze można wymagać stworzenia val, które muszą być ustawione za pomocą metody init(). Można zdecydować się nie robić tego ostatniego i pojawia się błąd w czasie wykonywania, ale przynajmniej nie zapomnisz go w całości:

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() 
} 

Bez oszukiwania !:

> 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 } 

Zaletą tego ma Jeśli potrzebujesz wielu rzeczy, które musisz zainstalować, zanim zainicjujesz wszystkie razem, lub jeśli Twoja klasa Component to final, więc nie możesz mieszać się z niczym innym.

1

pomysłem może być użycie trick opisany tutaj: Cake pattern: how to get all objects of type UserService provided by components

wszystkich elementów, które powinny być inicjowane może być zarejestrowany w niektórych Seq[InitializableComponent]. A potem możesz zainicjować wszystkie zarejestrowane komponenty za pomocą foreach.

Żaden ze składników nie zostaną zapomniane w tym Seq ponieważ są rejestrowane automatycznie, ale nadal można zapomnieć zadzwonić do foreach tak ...