说我想通过特征来定义球的特征:
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!
换句话说,我可以在保留其他特征的同时覆盖特定特征吗?
答案 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)
等,但是如果它更多地涉及特定形状的属性,则可能需要更专业的特征,例如Polygon
或Regular
,以及类
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))
但是不能再将一个圆圈“变异”为一个正方形-这完全没有意义。