2012-11-11 14 views
5

Sono nuovo di Scala e non so perché devo fare un cast di tipo (non intuitivo per me) relativo ai tipi dipendenti dal percorso nel seguente codice. (non mi piace getter, setter né nulli, sono qui per operazioni separate e disambiguare la fonte di errori)Come evitare tipi casuali di lavoro che funzionano con tipi dipendenti dal percorso?

// Module A public API 
class ModA { 
    trait A 
} 

// Module B public API that depends on types defined in Module A 
class ModB(val modA: ModA) { 
    trait B { 
    def getA: modA.A; 
    def setA(anA: modA.A); 
    } 
} 

// One implementation of Module A 
class ModAImpl extends ModA { 
    class AImpl extends A 
} 

// One implementation of Module B 
class ModBImpl(mod: ModA) extends ModB(mod) { 
    class BImpl extends B { 
    private[this] var privA: modA.A = _; 
    override def getA = privA; 
    override def setA(anA: modA.A) = privA = anA; 
    } 
} 

object Main { 
    def main(args: Array[String]): Unit = { 
    // wiring the modules 
    val modAImpl = new ModAImpl; 
    val modBImpl = new ModBImpl(modAImpl); 

    // wiring objects 
    val a = new modAImpl.AImpl; 
    val b = new modBImpl.BImpl; 
    b.setA(a); //don't compile and complain: type mismatch; found: modAImpl.A required: modBImpl.modA.A 

    //i have to do this horrible and coutnerintuitive cast to workaround it 
    b.setA(a.asInstanceOf[modBImpl.modA.A]); 

    var someA: modAImpl.A = null; 
    someA = b.getA; // don't compile with same reason 
    someA = b.getA.asInstanceOf[modAImpl.A]; // horrible cast to workaround 

    println(a == b.getA); // however this prints true 
    println(a eq b.getA); // this prints true too 
    } 
} 

Ho letto sui tipi di Singleton a informare il compilatore quando due tipi sono gli stessi, ma non so come applicarlo qui. Grazie in anticipo.

risposta

5

Si può attaccare un parametro di tipo sui ModB tipi:

class ModA { trait A } 

class ModB[AA](val modA: ModA { type A = AA }) { 
    trait B { 
    def getA: AA 
    def setA(anA: AA) 
    } 
} 

class ModAImpl extends ModA { class AImpl extends A } 

class ModBImpl[AA](
    mod: ModA { type A = AA }) extends ModB(mod) { 
    class BImpl extends B { 
    private[this] var privA: AA = _ 
    override def getA = privA 
    override def setA(anA: AA) = privA = anA 
    } 
} 

E l'inferenza di tipo tutto funziona come desiderato:

scala> val modAImpl = new ModAImpl 
modAImpl: ModAImpl = [email protected] 

scala> val modBImpl = new ModBImpl(modAImpl) 
modBImpl: ModBImpl[modAImpl.A] = [email protected] 

scala> val a = new modAImpl.AImpl 
a: modAImpl.AImpl = [email protected] 

scala> val b = new modBImpl.BImpl 
b: modBImpl.BImpl = [email protected] 

scala> b.setA(a) 
+0

Molto grazie per la risposta Travis, ma la soluzione funziona solo all'interno l'ambito della classe, all'interno di un ambito del metodo (come nell'esempio che avevo originariamente esposto) non funziona. la riga: 'val modBImpl = new ModBImpl (modAImpl);' che in precedenza compila, ora si lamenta con: tipo mancata corrispondenza; trovato: ModAImpl richiesto: M forSome {tipo M <: ModA {tipo A = this.A}} e la riga 'b.set (a)' ora si lamenta con: tipo mancata corrispondenza; trovato: modAImpl.A richiesto: this.A –

+0

In questo caso è possibile fornire esplicitamente il parametro type ('val modBImpl = new ModBImpl [modAImpl.A] (modAImpl)'). Il che non è soddisfacente, ma funziona, ed è meglio del casting. –

+0

Scusa Travis, ma la riga di codice appena scritta non viene compilata. Nonostante ciò, si supponga che il tratto ModA abbia molti membri, non solo A. Ho bisogno di tutti i membri di ModA disponibili su ModB. –

Problemi correlati