2010-11-07 31 views
5

J'ai eu quelques difficultés à concevoir mes classes de cas. Une version simplifiée ressemble:Hiérarchie des classes de cas Scala

abstract class Base(s: Option[String]) { 
    //code 
} 

case class CaseClass(s: Option[String] = None) extends Base(s) { 
    //code 
} 

Et j'ai une méthode où je veux faire quelque chose comme:

def method(base : Base) = { 
    //code 
    base copy (s = Some("string")) 
    } 

Bien sûr, je reçois:

value copy is not a member of Base 

donc ce que je veux do est créer une nouvelle instance basée sur ma classe de base (qui n'est pas une classe de cas). Évidemment, on ne peut pas faire ça. Mais comment résolvez-vous cela d'une manière élégante?

Merci d'avance!

+0

Question connexe: http://stackoverflow.com/questions/2911562/case-class-copy-method-abstraction –

+1

http://scala-programming-language.1934581.n4.nabble.com/Question-on- case-class-and-copy-method-td1936310.html –

Répondre

3

Le comportement que vous essayez d'atteindre n'est pas implémentable. copy méthode d'une classe de cas est générée automatiquement par le compilateur, et une fois que vous ajoutez une méthode appelée copy à votre implémentation, le compilateur ne générera pas de sucre.

Vous pouvez réimplémentez copy avec des traits, mais il ne sera pas aussi flexible que le produit d'un (vous devez mettre à jour le trait de base, copy et method mises en œuvre chaque fois que l'ensemble sur le terrain d'un changement de classe de cas):

sealed trait Base[T] { 
    val s: Option[String] 
    def copy(s: Option[String]) : T 
} 

case class CaseClass(override val s: Option[String] = None) extends Base[CaseClass] { 
    override def copy(s: Option[String]) = CaseClass(s) 
} 

def method[T <: Base[T]](base : Base[T]) = base copy (s = Some("strng")) 

vous pouvez également mettre en œuvre method comme suit:

case class CaseClass(s: Option[String] = None) 

def method[X <: {def copy(s: Option[String]):X}](base : X) = 
    base copy(s = Some("string")) 

scala> method(CaseClass()) 
res4: CaseClass = CaseClass(Some(string)) 

Ainsi, vous n'aurez pas besoin du trait Base et réduisez le nombre de modifications si vos classes de cas changent.

+0

J'aime la première suggestion. Merci! – tbruhn

7

Si vous paramétrez votre classe de base et y définissez également la méthode de copie abstraite, vous pouvez demander aux sous-classes de renvoyer des instances de leurs propres types à partir de la méthode de copie. Dans ce cas, vous voulez que CaseClass renvoie une CaseClass, vraisemblablement.

abstract class Base[T](s: Option[String]) { 
    def copy(in: Option[String]) : T 
} 

case class CaseClass(s: Option[String]) extends Base[CaseClass](s) { 
    def copy(in: Option[String]) = CaseClass(in) 
} 

case class OtherClass(s: Option[String]) extends Base[OtherClass](s) { 
    def copy(in: Option[String]) = OtherClass(in) 
} 

def method[T <: Base[T]](base: T) : T = { 
    base.copy(Some("String")) 
} 


scala> method(CaseClass(None)) 
res1: CaseClass = CaseClass(Some(String)) 

scala> method(OtherClass(Some("hi"))) 
res2: OtherClass = OtherClass(Some(String)) 

D'autres sous-classes de Base renvoient leurs propres types. Le paramètre type sur #method est défini avec une limite supérieure de Base [T]. Cela signifie que T doit être un sous-type de Base [T] et est ce qui vous permet de fournir des instances de CaseClass et OtherClass en tant que paramètres de cette méthode.