2010-10-07 9 views
4

Mise à jour: décantées et élargi, étant donné que la question initiale a été simplifiée trop loinScala génériques récursives: Parent [enfant] et enfant [Parent]

je besoin d'une paire de traits, dont chacun se réfère à l'autre tel que les classes parentales et les enfants doivent être en relation les uns avec les autres.

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child [P <: Parent] { 
    def parent: P = ... 
    def bar = parent.foo(this) 
} 

telle que les cours de mise en œuvre doivent venir par paires:

class ActualParent extends Parent [ActualChild] { 
    def foo(c: ActualChild) = ... 
} 

class ActualChild extends Child [ActualParent] { 
} 

Malheureusement, le compilateur n'aime pas ces traits parce que les types génériques ne sont pas complets. Au lieu de il faut dire C <: Child[quelque chose]. les laissant sans précision ne fonctionne pas non plus:

trait Parent [C <: Child[_]] { 
    def foo(c: C) 
} 

trait Child [P <: Parent[_]] { 
    def parent: P = ... 
    def bar = parent.foo(this) 
} 

Il se plaint maintenant sur la ligne parent.foo(this), parce qu'il ne sait pas que this est du type correct. Le type de parent doit être Parent[this.type] pour l'appel au foo pour avoir les bons types.

Je pense qu'il doit y avoir une façon de faire référence au propre type d'un objet? Ou à un type qui doit être lui-même?


Mise à jour: Suite à @ la réponse de Daniel, j'ai essayé d'utiliser un membre de type abstrait chez l'enfant d'indiquer les types génériques de type parent comme celui-ci:

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent[this.type] 

    def parent: P = ... 
    def bar = parent.foo(this) 
} 

Ce ISN « t travailler lorsque je tente de la mettre en œuvre:

class ActualParent extends Parent [ActualChild] { 
    def foo(c: ActualChild) = ... 
} 

class ActualChild extends Child { 
    type P = ActualParent 
} 

donne l'erreur suivante:

overriding type Parent in trait Child with bounds >: Nothing <: Parent[ActualChild.this.type] 
type Parent has incompatible type 

Qu'est-ce que cela signifie?

Répondre

5

Vous pouvez utiliser l'approche donnée dans http://programming-scala.labs.oreilly.com/ch13.html:

abstract class ParentChildPair { 
    type C <: Child 
    type P <: Parent 

    trait Child {self: C => 
    def parent: P 
    } 

    trait Parent {self: P => 
    def child: C 
    } 
} 

class ActualParentChildPair1 { 
    type C = Child1 
    type P = Parent1 

    class Child1 extends Child {...} 

    class Parent1 extends Parent {...} 
} 
+0

Cela semble correct. Maintenant, comment pourriez-vous développer cela pour inclure les chaînes de parent -> enfant -> enfant? –

+0

Cette approche est couronnée de succès, mais je ne me sens pas très satisfait. Je ne suis pas sûr de pouvoir expliquer pourquoi. –

1

Vous pouvez écrire C <: Child[_].

+1

Je suppose que cela fonctionnera, mais je pense que l'OP est après un raccourci pour parent [C Synesso

+0

Comme Synesso l'a dit, Child [_] n'appliquera pas que les parents et les enfants soient appariés les uns aux autres. –

+0

Pourquoi voulez-vous utiliser des génériques? Pourquoi ne pas utiliser les paramètres: class Parent (c: Child); class Child (p: Parent) – sschaef

3

Cela peut être fait avec des membres de type abstrait.

class Parent { 
    type C <: Child 
    def child: C = null.asInstanceOf[C] 
} 

class Child { 
    type P <: Parent 
    def parent: P = null.asInstanceOf[P] 
} 
+0

Cela me fait passer l'avertissement du compilateur, mais n'applique pas la condition que parent et la classe enfant doivent chacun se rapporter à l'autre. –

+0

Voir ma réponse en fonction des membres de type abstrait. –

3

Suite à @ réponse de Daniel, je peux utiliser un membre de type abstrait chez l'enfant d'indiquer les types génériques de type parent comme celui-ci :

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent[this.type] 

    def parent: P = ... 
    def bar = parent.foo(this) 
} 

this.type n'est pas utilisable directement dans les génériques, mais dans un paramètre, il semble bien. Cette approche est beaucoup moins longue que la classe abstraite environnante, et permet des utilisations plus flexibles telles que les enfants qui sont aussi des parents.

1

Même si cela n'a pas réussi, je vais enregistrer cette avenue comme une réponse.

utilisant des éléments de type abstrait, les limites de l'Etat pour les types qui renvoient à this.type:

trait Parent { 
    type C <: Child { type P <: this.type } 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent { type C <: this.type } 
    def parent: P 
    def bar = parent.foo(this) 
} 

class ActualParent extends Parent { 
    type C = ActualChild 
    def foo(c: ActualChild) = println("Hello") 
} 

class ActualChild extends Child { 
    type P = ActualParent 
    def parent = new ActualParent 
} 

Le problème ici est que le compilateur ne se connecte pas this avec le type d'enfant du parent, afin d'appeler parent.foo(this) résultats dans:

type mismatch 
found : Child.this.type (with underlying type Child) 
required: _3.C where val _3: Child.this.P