12

私は Scala にかなり慣れていないので、API ライブラリを作成し、val と不変オブジェクト (可能な場合) のみを使用しようとしています。オブジェクトをモデル化する正しい方法を決定しようとしています。いくつかの「Post」オブジェクトがあり、すべてがいくつかの共通フィールドを共有し、それぞれが独自の特定のフィールドを追加するとします。

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, ...)

このメソッドは、多くの反復的な宣言とボイラープレート コードを追加します。特に、抽象クラスで宣言する多数の Post サブクラスと共有フィールドを処理する場合です。私の見方では、これは、コンストラクターによってのみ初期化できる vals の使用が原因です。ここで詳述されている関係を作成するためのより良い方法はありますか、それとも不変オブジェクトのみを使用したいためにこれに苦しむ必要がありますか?

4

1 に答える 1

13

まず第一にval、case-class コンストラクターの修飾子は冗長です。Scala コンパイラーはそれらを既に「無料」で提供しています (これは case-classes の機能の 1 つです)。実際にコンパイルしようとすると、宣言されていないオーバーライドのためにエラーが発生することがわかります。

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 )
                                ^

個人的には、コンストラクター引数をできるだけ少なくして抽象クラスを使用することをお勧めします。Scala には、ここでより良い仕事をする特徴があります。コンストラクターの引数の代わりに、フィールドを (抽象的に) 宣言します。その後、ケースクラスが開始され、それらが引数を自動的valsに作成するため、完了です。

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

さらに、 に追加sealedすると、誤ってケースを見逃すことなく、パターン マッチングでtrait Postのサブタイプを安全に使用できます。Post

于 2012-09-05T22:09:57.477 に答える