Scala 使用traits和with关键字进行代码缩减

Scala 使用traits和with关键字进行代码缩减,scala,traits,reduce,Scala,Traits,Reduce,我有一些超类型的课程。因此,所有这些类都必须重写相同的方法。现在,我可以调用一个方法并将其提交为通用超类型的对象。但对每个提交的类型做出反应并不总是有用的,因此会引发异常。首先,我试图像这样解决这种行为: def operation(s: SuperType) = s match { case t: SubType1 => ... case t: SubType2 => ... case _ => ... } class Supertype[A <: Sup

我有一些超类型的课程。因此,所有这些类都必须重写相同的方法。现在,我可以调用一个方法并将其提交为通用超类型的对象。但对每个提交的类型做出反应并不总是有用的,因此会引发异常。首先,我试图像这样解决这种行为:

def operation(s: SuperType) = s match {
  case t: SubType1 => ...
  case t: SubType2 => ...
  case _ => ...
}
class Supertype[A <: Supertype] {
    def operation(s: A) {

    }
}

class Subtype extends SuperType[Subtype] {
    override def operation(s: Subtype) {

    }
}
由于有很多子类型,这将导致大量代码(在每个方法和每个类中),我试图用
traits
解决这个问题。每个trait应该只测试一个类型,然后将对象转发到堆栈上更高的方法。下面的代码描述了我是如何想象的。但这不起作用,因为编译器无法分解类型。另一个问题是,我必须声明每个行为类中类的每个属性

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
  }
}
有人能告诉我是否有可能通过使用traits来减少代码行吗?还是我目前使用的“全部匹配”解决方案是最好的

编辑:

在你的帮助下,我找到了一个半途而废的解决办法。我的主要问题是,我试图通过使用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)
}
最终对象跟踪测试{
def main(参数:数组[字符串]){
导入traitwith.operations_
val e1=偶数(2,4)
val e2=偶数(1,3)
val o1=奇数(1.25,3.75)
val o2=奇数(7.25,9.25)
val a1=全部(5.5)
val a2=全部(3.5)
val n1=毫厘(o1)
val n2=努姆霍尔(a1)
println(“e1+e2:+add(e1,e2))
println(“o1+o2:+添加(o1,o2))
尝试{println(“e1+o2:+add(e1,o2))}捕获{case e=>println(e)}
println(“o1+e2:+添加(o1,e2))
试试{println(“a1+e2:+add(a1,e2))}catch{case e=>println(e)}
println(“n1+n2:+add(n1,n2))
}
}
最终对象操作{
定义添加(a:Num,b:Num)=a->b匹配{
案例(a1:奇数,b1:奇数)=>a1+b1
案例(a1:奇数,b1:偶数)=>奇数(a1.x+b1.x,a1.y+b1.y)
案例(a1:奇数,b1:全部)=>奇数(a1.x+b1.x,a1.y+b1.x)
情况(a1:偶数,b1:偶数)=>a1+b1
案例(a1:All,b1:All)=>a1+b1
案例=>错误(“无法将“+b+”添加到“+a”)
}
}
抽象类Num{

类型A您的问题是,您试图在非面向对象的设计中使用面向对象的特性,例如类和继承

OOP的全部要点是,你不需要反省类是什么。而是使用多态性来实现结果。我特别喜欢说明OO应该如何工作,但在这方面并不缺乏资源

编辑

例如,所提供的代码大致翻译为以下内容,减去不起作用的内容(所提供的代码不能准确编译是因为它们)


在我看来,它可以通过访问者模式进一步改进,这是有意义的,因为它针对的问题与类型匹配通常所针对的问题相同。

泛型可能会对您有所帮助。请尝试以下方法:

def operation(s: SuperType) = s match {
  case t: SubType1 => ...
  case t: SubType2 => ...
  case _ => ...
}
class Supertype[A <: Supertype] {
    def operation(s: A) {

    }
}

class Subtype extends SuperType[Subtype] {
    override def operation(s: Subtype) {

    }
}

类超类型[A误引某个广告:有一个类型类用于该广告

Scala已经通过
Numeric
支持“数字”上的即席多态性,这可能是您真正想要的:

但是,如果这个偶数/奇数/全部方案是您实际正在做的,而不仅仅是一个人为的示例,那么您总是可以滚动您自己的类型类


我们称之为可添加的
Addable

case class Even(x:Int, y:Int)
case class Odd(x:Double, y:Double)
case class All(x:Double)

abstract class Addable[A, B] {
  def add(a: A, b: B): A
}

implicit object EvenCanAddEven extends Addable[Even, Even] {
  def add(a:Even, b:Even) = Even(a.x+b.x, a.y+b.y)
}

implicit object OddCanAddOdd extends Addable[Odd, Odd] {
  def add(a:Odd, b:Odd) = Odd(a.x+b.x, a.y+b.y)
}

implicit object OddCanAddEven extends Addable[Odd, Even] {
  def add(a:Odd, b:Even) = Odd(a.x+b.x, a.y+b.y)
}

implicit object AllCanAddAll extends Addable[All, All] {
  def add(a:All, b:All) = All(a.x+b.x)
}

def add[A,B](a:A, b:B)(implicit tc: Addable[A,B]) =
  tc.add(a, b)


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: " + add(e1, e2))
println("o1 + o2: " + add(o1, o2))
println("e1 + o2: " + add(e1, o2)) //compiler should fail this line
println("o1 + e2: " + add(o1, e2))
println("a1 + e1: " + add(a1, e2))

免责声明:我还没有实际测试代码,这台机器还没有安装Scala

一种替代解决方案,用于直到运行时才知道类型的情况:

sealed trait Num
case class Even(x:Int, y:Int) extends Num
case class Odd(x:Double, y:Double) extends Num
case class All(x:Double) extends Num

object operations {
  def add(a: Num, b: Num) : Num = (a,b) match {
    case (a1:Even, b1:Even) => Even(a1.x+b1.x, a1.y+b1.y)
    case (a1:Odd, b1:Odd) => Odd(a1.x+b1.x, a1.y+b1.y)
    case (a1:Odd, b1:Even) => Odd(a1.x+b1.x, a1.y+b1.y)
    case (a1:All, b1:All) => All(a1.x, b1.x)
    case _ => error("can't add " + a + " to " + b)
  }
}
这里的技巧是首先将两个参数包装成一个元组,这样就有了一个要进行模式匹配的对象

更新

编辑之后,您似乎不需要抽象类型
A
任何地方,为什么不将
Num
作为标记特征,并在每个子类中分别定义+方法呢

sealed abstract trait Num

case class Odd(x: Double, y: Double) extends Num {
  def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  def +(a: NumHolder) = NumHolder(x + a.x)
}

我不知道我是否能解决您的问题,但在思考这个问题时,我尝试至少让您的示例编译并运行,希望这至少会有所帮助

我以
toString
方法的形式添加了一些杂讯,以便能够看到实例化和表达式的结果

abstract class Num(val a: Double, val b: Double) {
  def +(that: Num): Num
  override def toString = (<z>Num({a}, {b})</z> text)
}
现在,
偶数
s和
奇数
s的工作方式可以被提炼成特征,但这对于这个例子来说是不必要的。不这样做简化了继承,但可能与这个例子的要点背道而驰,我不知道

偶数
知道如何处理
偶数
奇数
项,但会将任何其他项传递给其超类。同样,出于匹配目的,它是一个假大小写类

class Even(override val a: Double, override val b: Double) extends All(a, b) {
  override def +(that: Num): Num = that match {
    case Even(a, b) => Even(this.a + a, this.b + b)
    case Odd(a, b)  => Odd(this.a + a, this.b + b)
    case x => super.+(x)
  }
  override def toString = (<z>Even({a}, {b})</z> text)
}
object Even {
  def apply(a: Double, b: Double) = new Even(a, b)
  def unapply(num: Even) = Some((num.a, num.b))
}
好的,让我们转一转

object Try {
  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("e1 + o2: " + (e1 + o2))
    try { println("o1 + o2: " + (o1 + o2)) } catch { case e => println(e) }
    println("o1 + e2: " + (o1 + e2))
    println("a1 + e1: " + (a1 + e2))
  }
}
根据Kevin Wright的理论,我现在解决了这个问题:

package de.traitwith

import de.traitwith.Operations._
import de.traitwith._

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)
    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))
    println("o1 + n2: " + add(o1, n2))
  }
}

object Operations {
  def add(a: Num, b: Num): 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: Odd, b1: NumHolder) => add(a1, b1.x)
    case (a1: Even, b1: Even) => a1 + b1
    case (a1: Even, b1: NumHolder) => add(a1, b1.x)
    case (a1: All, b1: All) => a1 + b1
    case (a1: All, b1: NumHolder) => add(a1, b1.x)
    case (a1: NumHolder, b1: NumHolder) => a1 + b1
    case (a1: NumHolder, b1: Odd)=> add(a1.x, b1)
    case (a1: NumHolder, b1: Even) => add(a1.x, b1)
    case (a1: NumHolder, b1: All) => add(a1.x, b1)
    case _ => error("can't add " + b + " to " + a)
  }
}

abstract class Num

final case class Odd(x: Double, y: Double) extends Num {
  def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  def +(a: NumHolder) = NumHolder(add(x, a.x))
}

我在超级类型中没有更多的方法-我希望,当天,这不会导致问题。可以删除类中的所有add方法,但在我的实际应用程序中,我有更大的类,所以我需要它们。

我不明白多态性在这里有什么帮助。必须有可能,例如,可以添加所有对象都是偶数和奇数到奇数。每个类都必须对提交的参数做出反应。我可以使用重载方法,而不是使用match关键字。但这没有什么区别。通过多态性和无重载,一个类只能对一种类型作出反应,而不能对几种类型作出反应。您能否解释一下您首先要实现的目标?这些类包含不同的数学值。例如有理数、复数、向量、矩阵……当一个值被添加到另一个值时,有不同的“方法”可以这样做,所以我必须检查值的类型
package de.traitwith

import de.traitwith.Operations._
import de.traitwith._

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)
    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))
    println("o1 + n2: " + add(o1, n2))
  }
}

object Operations {
  def add(a: Num, b: Num): 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: Odd, b1: NumHolder) => add(a1, b1.x)
    case (a1: Even, b1: Even) => a1 + b1
    case (a1: Even, b1: NumHolder) => add(a1, b1.x)
    case (a1: All, b1: All) => a1 + b1
    case (a1: All, b1: NumHolder) => add(a1, b1.x)
    case (a1: NumHolder, b1: NumHolder) => a1 + b1
    case (a1: NumHolder, b1: Odd)=> add(a1.x, b1)
    case (a1: NumHolder, b1: Even) => add(a1.x, b1)
    case (a1: NumHolder, b1: All) => add(a1.x, b1)
    case _ => error("can't add " + b + " to " + a)
  }
}

abstract class Num

final case class Odd(x: Double, y: Double) extends Num {
  def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  def +(a: NumHolder) = NumHolder(add(x, a.x))
}