12

おそらく、スタイルとエレガンスに優れた Scala の専門家が、コンストラクターの「プッシュアウト」問題がある次のコードを構成するためのより良い方法を見つけ出すのを手伝ってくれるでしょう。

単純な基本クラスから始めます。

class Foo(val i: Int, val d: Double, val s: String) {

  def add(f: Foo) = new Foo(i + f.i, d + f.d, s + f.s)
  override def toString = "Foo(%d,%f,%s)".format(i,d,s)

}

複雑なアプリケーションで型チェックを行うために、状態を追加しないサブクラスが必要です。

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

}

現状では、2 つのバーを追加すると、Foo のみが返されます。

val x = new Bar(1,2.3,"x")
val y = new Bar(4,5.6,"y")
val xy = x.add(y)

REPL で次の応答を使用します。

x  : Bar = Bar(1,2.300000,x)
y  : Bar = Bar(4,5.600000,y)
xy : Foo = Foo(5,7.900000,xy)

以下のように、Foo の add メソッドをコピーして貼り付けることなく、2 つの Bar を追加して別の Bar (Foo ではなく) を形成するにはどうすればよいですか?

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  // ugly copy-and-paste from Foo:
  def add(b: Bar) = new Bar(i + b.i, d + b.d, s + b.s)
  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

}

私はそのようなバーをたくさん持っています (基本的にはすべて Foo のコピーですが、型チェックには非常に重要です)。カットアンドペーストのないソリューションは利益をもたらします。

ありがとう!

4

3 に答える 3

12

相続は極力避けようとしています。したがって、ここに別のアプローチがあります。

クラスBarのコンストラクターはとまったく同じでFoo、どちらもステートレスです。複数のサブタイプが必要な場合は、追加情報を伝えるためだけに、ジェネリックパラメーターを「ラベル」として使用できます。例えば:

trait Kind
trait Bar extends Kind

class Foo[T<:Kind](val i: Int, val d: Double, val s: String) {
   def add(f: Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s)
   override def toString = "Foo(%d,%f,%s)".format(i,d,s)
}


scala> val b1 = new Foo[Bar](2,3.0,"hello")
b1: Foo[Bar] = Foo(2,3.000000,hello)

scala> val b2 = new Foo[Bar](3,1.0," world")
b2: Foo[Bar] = Foo(3,1.000000, world)

scala> b1 add b2
res1: Foo[Bar] = Foo(5,4.000000,hello world)

addはタイプセーフです。次に、型クラスを使用して、toStringを表示することができますKind

于 2012-09-04T09:00:21.103 に答える
5

Bar@paradigmatic の回答を拡張して、それぞれに固有の操作(例: different )をサポートできるようにしたい場合はtoString、さらに一歩進んで型クラスを作成Kindできます。

trait Kind[T] { def name : String }
trait Bar
implicit object BarHasKind extends Kind[Bar] { val name = "Bar" }

class Foo[T : Kind](val i : Int, val d : Double, val s : String) {
  def add(f : Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s)
  override def toString = implicitly[Kind[T]].name + "(%d,%f,%s)".format(i,d,s)
}

scala> val b1 = new Foo[Bar](2, 3.0, "hello")
b1: Foo[Bar] = Bar(2,3.000000,hello)

trait Biz
implicit object BizHasKind extends Kind[Biz] { val name = "Biz" }

scala> val b2 = new Foo[Biz](1, 1.0, "One")

以前と同じようにタイプ セーフです。

scala> b1 add b2
<console>:16: error: type mismatch;
  found   : Foo[Biz]
  required: Foo[Bar]

scala> b2 add b2
resN: Foo[Biz] = Biz(2,2.000000,OneOne)

タグに依存させたいプロパティについては、それらを抽象的に宣言しKind、暗黙的なオブジェクトで実装を提供します。

于 2012-09-04T19:44:07.827 に答える
2

型のパラメータ化によるアプローチには、継承という自然な方法で機能を拡張できないという制限があります。したがって、別のアプローチとして、オーバーライド可能なファクトリ メソッド (コンストラクターへのデリゲートにすぎません) を使用できます。

class Foo(val i: Int, val d: Double, val s: String) {

  protected def create(i: Int, d: Double, s: String) = new Foo(i, d, s)

  def add[A <: Foo](f: A) = create(i + f.i, d + f.d, s + f.s)

  override def toString = "Foo(%d,%f,%s)".format(i,d,s)
}

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  protected override def create(i: Int, d: Double, s: String) = new Bar(i, d, s)

  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

  // additional methods...

}

println( new Foo(10, 10.0, "10") add new Bar(10, 10.0, "10") )
println( new Bar(10, 10.0, "10") add new Foo(10, 10.0, "10") )
于 2012-09-07T13:20:19.693 に答える