同じスーパータイプのクラスがいくつかあります。したがって、このクラスはすべて同じメソッドをオーバーライドする必要があります。これで、メソッドを呼び出して、共通のスーパータイプのオブジェクトとしてコミットできます。ただし、コミットされた各タイプに反応することが常に役立つとは限らないため、例外がスローされます。最初に、この動作を次のように解決しようとしました:
def operation(s: SuperType) = s match {
case t: SubType1 => ...
case t: SubType2 => ...
case _ => ...
}
サブタイプが多いため、(各メソッドおよび各クラスで) 多くのコードが必要になるため、この問題を で解決しようとしましたtraits
。各特性は 1 つの型のみをテストし、オブジェクトをスタックの上位のメソッドに転送する必要があります。以下のコードは、私がそれをどのように想像するかを説明しています。ただし、コンパイラは型を分解できないため、これは機能しません。もう 1 つの問題は、各動作クラスでクラスの各属性を宣言する必要があることです。
object TraitWithTest {
def main(args: Array[String]) {
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
println("e1 + e2: " + (e1 + e2))
println("o1 + o2: " + (o1 + o2))
try { println("e1 + o2: " + (e1 + o2)) } catch { case e => println(e) }
println("o1 + e2: " + (o1 + e2))
println("a1 + e1: " + (a1 + e2))
}
}
abstract class Num {
def +(n: Num): Num
}
trait OddBehaviour extends Num {
val e1, e2: Int // here I don't want to declare all attributes
val a1: Double
abstract override def +(n: Num) = n match {
case o: Odd => throw new UnsupportedOperationException("Even#+(Odd)")
case _ => super.+(n)
}
}
trait EvenBehaviour extends Num {
val o1, o2: Double
val a1: Double
abstract override def +(n: Num) = n match {
case e: Even => Odd(o1 + e.e1, o2 + e.e2)
case _ => super.+(n)
}
}
trait AllBehaviour extends Num {
val o1, o2: Double
val e1, e2: Int
abstract override def +(n: Num) = n match {
case a: All => Odd(o1 + a.a1, o2 + a.a1)
case _ => super.+(n)
}
}
object Even {
def apply(e1: Int, e2: Int) = new Even(e1, e2) with OddBehaviour with AllBehaviour
}
abstract case class Even(e1: Int, e2: Int) extends Num {
override def +(n: Num) = n match {
case c: Even => Even(e1 + c.e1, e2 + c.e2)
case _ => throw new IllegalArgumentException
}
}
object Odd {
def apply(o1: Double, o2: Double) = new Odd(o1, o2) with EvenBehaviour with AllBehaviour
}
abstract case class Odd(o1: Double, o2: Double) extends Num {
override def +(n: Num) = n match {
case o: Odd => Odd(o1 + o.o1, o2 + o.o2)
case _ => throw new IllegalArgumentException
}
}
object All {
def apply(a1: Double) = new All(a1) with EvenBehaviour with OddBehaviour
}
abstract case class All(a1: Double) extends Num {
override def +(n: Num) = n match {
case a: All => All(a1 + a.a1)
case _ => throw new IllegalArgumentException
}
}
特性を使用してコード行を減らすことができるかどうか誰か教えてもらえますか? または、現在使用しているすべてのソリューションが最適ですか?
編集:
あなたの助けを借りて、半分有効な解決策を見つけました。私の主な問題は、Scala 機能を使用してコード行を削減しようとしたことです。そのため、最も簡単な方法を見落としていました。それは、コードをアウトソーシングすることです! オブジェクトの組み合わせをチェックする新しいオブジェクトを作成するだけです。オブジェクト自体は、独自の型のみを処理します。
これはコードです:
final object TraitWithTest {
def main(args: Array[String]) {
import traitwith.operations._
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
val n1 = NumHolder(o1)
val n2 = NumHolder(a1)
println("e1 + e2: " + add(e1, e2))
println("o1 + o2: " + add(o1, o2))
try { println("e1 + o2: " + add(e1, o2)) } catch { case e => println(e) }
println("o1 + e2: " + add(o1, e2))
try { println("a1 + e2: " + add(a1, e2)) } catch { case e => println(e) }
println("n1 + n2: " + add(n1, n2))
}
}
final object operations {
def add(a: Num, b: Num) = a -> b match {
case (a1: Odd, b1: Odd) => a1 + b1
case (a1: Odd, b1: Even) => Odd(a1.x + b1.x, a1.y + b1.y)
case (a1: Odd, b1: All) => Odd(a1.x + b1.x, a1.y + b1.x)
case (a1: Even, b1: Even) => a1 + b1
case (a1: All, b1: All) => a1 + b1
case _ => error("can't add " + b + " to " + a)
}
}
abstract class Num {
type A <: Num
def +(a: A): A
}
final case class Odd(x: Double, y: Double) extends Num {
override type A = Odd
override def +(a: Odd) = Odd(x + a.x, y + a.y)
}
final case class Even(x: Int, y: Int) extends Num {
override type A = Even
override def +(a: Even) = Even(x + a.x, y + a.y)
}
final case class All(x: Double) extends Num {
override type A = All
override def +(a: All) = All(x + a.x)
}
final case class NumHolder(x: Num) extends Num {
override type A = NumHolder
override def +(a: NumHolder) = NumHolder(x + a.x)
}
コードを少し拡張して、 object を挿入しましたNumHolder
。ここで、小さな欠陥が 1 つだけあります。NumHolder では、追加メソッドでコンパイル エラーが発生しない限り、スーパータイプをコミットできません。type-keyword の代わりに Generics を使用しようとしましたが、常に型を Num に設定する必要があるため (オブジェクト操作でも)、これは不便です。
この小さなコンパイル エラーを解決するにはどうすればよいですか?