2012-09-05 11 views
8

Ich bin ziemlich neu in Scala, und schreibe eine API-Bibliothek und versuche, nur Vals und unveränderliche Objekte (wo möglich) zu verwenden. Ich versuche, den richtigen Weg für die Modellierung der Objekte zu finden. Angenommen, es gibt mehrere „Post“ Objekte, die alle einige gemeinsame Felder und jeder fügt seine eigenen spezifischen Felder:Scala Extending Parametrisierte Zusammenfassung Klasse

abstract class Post(val id: Int, val name: String, ...) 
case class TextPost(val id: Int, val name: String, ..., val text: String) extends Post(id, name, ...) 
case class PhotoPost(val id: Int, val name: String, ..., val url: String) extends Post(id, name, ...) 

Diese Methode viele sich wiederholende Erklärungen ergänzt und Kesselblech Code, vor allem im Umgang mit vielen Post-Unterklassen und freigegebene Felder deklarieren in der abstrakten Klasse. So wie ich es sehe, wird dies durch die Verwendung von Vals verursacht, die nur vom Konstruktor initialisiert werden können. Gibt es eine bessere Möglichkeit, die hier aufgeführten Beziehungen zu erstellen, oder muss ich darunter leiden, dass ich nur unveränderliche Objekte verwenden möchte?

Antwort

8

Zunächst sind die val Modifikatoren in den Fallklassenkonstruktoren redundant, der Scala Compiler wird sie Ihnen bereits "kostenlos" geben (es ist eine der Eigenschaften von Fallklassen). Wenn Sie tatsächlich versucht, das zu kompilieren, werden Sie sehen, dass ein Fehler wegen nicht angemeldeter überwiegendes auftritt:

abstract class Post(val id: Int, val name: String) 
case class TextPost(id: Int, name: String) extends Post(id, name) 
case class PhotoPost(id: Int, name: String) extends Post(id, name) 

<console>:10: error: overriding value id in class Post of type Int; 
value id needs `override' modifier 
      case class PhotoPost(id: Int, name: String) extends Post(id, name) 
           ^

ich persönlich empfehlen abstrakte Klassen zu verwenden, mit Konstruktorargumente so wenig wie möglich. Scala hat Eigenschaften, die hier besser funktionieren. Anstelle von Konstruktorargumenten deklarieren Sie (abstrakt) die Felder; dann treten die Fallklassen und aufgrund sie ihre Argumente automatisch vals machen, sind Sie fertig:

trait Post { def id: Int; def name: String } 
case class TextPost(id: Int, name: String, text: String) extends Post 
case class PhotoPost(id: Int, name: String, url: String) extends Post 

Außerdem, wenn Sie sealed zum trait Post hinzufügen, können Sie sicher die Subtypen von Post in Pattern-Matching, ohne versehentlich verwenden können fehlende Fälle.

+0

Danke für die Antwort! In Bezug auf den Kode des Kessels bedeutet diese Methode, dass ich jeden Parameter in abgeleiteten Klassen nicht als val deklariere, aber ich muss trotzdem alle Parameter in jeder Deklaration wiederholen, richtig? Ich betone das, weil ich 8 Unterklassen und 15 geteilte Felder habe, und das scheint so ein riesiger DRY-Fall zu sein. Aber ich verstehe aus Ihrer Antwort, dass es keinen Weg gibt, richtig? – orrsella

+1

Die einzige andere Alternative ist es, die gemeinsame Schnittstelle nicht zu implementieren ('Post'). Das heißt, Sie müssen den Typ _compose_ eingeben, um ihn zu enthalten, aber ich weiß nicht, ob Sie das wollen: 'case class Post (id: Int, name: String); Fallklasse TextPost (Beitrag: Post, Text: String) '. –

+5

Ich glaube nicht, dass diese Antwort ausreichend ist, da sie die Frage nach der Kesselplatte nicht beantwortet ... –

Verwandte Themen