2013-04-27 22 views
5

Ho bisogno di un meccanismo intelligente per la composizione dei componenti che consenta caratteri misti per inizializzare dopo il componente composto. La seguente tiri un NullPointerException:Inizializzazione del carattere di ritardo

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 

Le seguenti cose sono non opzioni per me:

  • Uso protected lazy val component; quello produrrebbe un di valanga di dozzine di vals che devono diventare pigri, qualcosa Io non voglio.
  • Inserendo addListener in un metodo, ad es. initDynamic(); perché mescolerò molti tratti e non voglio ricordare di chiamare una mezza dozzina di metodi initFoo().
  • Utilizzo di DelayedInit. Questo non funziona con i tratti, almeno secondo gli scaladoc.

potrei vivere con una sola init() chiamata, ma solo alle seguenti condizioni:

  • tutti mescolati in tratti può facilmente dichiarare ad essere utilizzate a questo singola chiamata
  • si tratta di una compilazione errore da dimenticare l'istruzione init().

risposta

10

È possibile ritardare l'inizializzazione di un tratto utilizzando definizioni iniziali. (Si veda la sezione 5.1.6 del scala language specification)

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

Ah, sì, ho dimenticato questa possibilità. Sfortunatamente, nel mio caso d'uso, sto avendo un'ulteriore indiretta: mi piace "AbstractFoo estende DynamicComponent" e "Foo estende AbstractFoo', dove il componente è fornito da" Foo "e i suoi metodi di pittura" richiamano "i metodi forniti da" AbstractFoo "- usando la definizione iniziale, 'Foo' non vede più alcun metodo da' AbstractFoo'. Hmm .... –

0

Ecco un'idea (Sono felice di leggere su altri suggerimenti):

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 

Quindi, fondamentalmente sto forzando la componente di mescolare in un tipo che può essere fornito solo dal misto in trait . Ragionevole? Sembra un po 'troppo complicato nei miei occhi.

2

E 'anche clunkier che la soluzione, ma si può sempre richiedere la creazione di una val che deve essere impostato con il metodo init(). Si potrebbe scegliere di non farlo scorso ed ottenere un errore in fase di esecuzione, ma almeno che non si dimentica del tutto:

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

Nessun inganno !:

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

Il vantaggio questo ha è se hai bisogno di più cose da mettere in atto prima di inizializzarle tutte in modo cooperativo, o se la tua classe è final, quindi non puoi mischiare qualcos'altro.

1

Un'idea potrebbe essere quella di utilizzare il trucco descritto qui: Cake pattern: how to get all objects of type UserService provided by components

Tutti i componenti che dovrebbero essere inizializzati potrebbero essere registrati in alcune Seq[InitializableComponent]. E poi puoi inizializzare tutti i componenti registrati con un foreach.

Nessun componente sarà dimenticato in che Seq perché vengono registrati automaticamente, ma si può comunque dimenticare di chiamare il foreach comunque ...

Problemi correlati