如何编写Scala特性?

时间:2018-12-06 15:32:41

标签: scala traits

说我想通过特征来定义球的特征:

trait Size {
  val size: String
}

trait Big extends Size {
  val size = "big"
}

trait Small extends Size {
  val size = "small"
}

trait Bouncy {
  def bounce: Unit = println("boing boing boing")
}

class Ball

val bigBouncyBall = new Ball with Big with Bouncy

到目前为止,太好了。但是如何在保持弹跳的同时改变球的大小?

def shrink(ball: Ball) = 
  ball with Small // invalid syntax. Does not compile

def shrink(ball: Ball) =
  ball.asInstanceOf[Ball with Small] // Nope. Ball no longer bounces!

换句话说,我可以在保留其他特征的同时覆盖特定特征吗?

1 个答案:

答案 0 :(得分:2)

您正在将对象的类型 properties 混合。特性旨在代表前者,而实例成员则代表后者。您可以将梨“变异”为苹果吗?那没有道理吧?

类似的东西:

  case class Ball(isBig: Boolean, isBouncy: Boolean) {
    def shrink = copy(isBig = false)
  }

在您的情况下,似乎是更明智的实现。 注意:这本身并不是对实例进行变异(在scala中通常不是一个好主意,应避免使用),而是使用修改后的属性。您可以 使其原地变异,但是,就像我说的那样,这不是一个好主意,因此,我不会去那里...

现在,哪些功能是类型的属性,哪些是实例的属性,并没有真正确定下来,这取决于将使用数据模型的应用程序的需求。

例如,如果应用程序处理不同的形状,但只关心顶点的数量,则可能只有

class Shape(val vertices: Int)
val circle = new Shape(0) 
val rect = new Shape(4)

等,但是如果它更多地涉及特定形状的属性,则可能需要更专业的特征,例如PolygonRegular,以及类

trait Regular { def size: Float }
case class Circle(val size: Float) extends Shape(0) with Regular
case class Square(val size: Float) extends Shape(4) with Regular with Polygon

这带有“优点”和“缺点”。一方面,您现在可以创建健壮的专用功能,仅处理特定类型的形状:

def perimeter(p: Polygon with Regular) = p.size * p.vertices 
def area(p: Polygon with Regular) = p.vertices * Math.pow(p.size, 2)/ (4 *  Math.tan(Math.PI/p.vertices))

但是不能再将一个圆圈“变异”为一个正方形-这完全没有意义。