我很好奇如何用类型类来做到这一点,所以我想出了这个版本,它甚至不允许你编译带有无效矩形的代码。我敢打赌这可以做得更干净一些,但这是我快速整理的:
trait LengthCalc[-A] {
def length(x: A): Double
}
trait WidthCalc[-A] {
def width(x: A): Double
}
trait AreaCalc[-A] {
def area(x: A): Double
}
case class Rectangle[A <: Option[Double], B <: Option[Double], C <: Option[Double]](lengthOpt: A = None, widthOpt: B = None, areaOpt: C = None)
(implicit lengthCalc: LengthCalc[Rectangle[A,B,C]], widthCalc: WidthCalc[Rectangle[A,B,C]], areaCalc: AreaCalc[Rectangle[A,B,C]]) {
lazy val length = lengthCalc.length(this)
lazy val width = widthCalc.width(this)
lazy val area = areaCalc.area(this)
}
implicit object RectLengthCalcFromLength extends LengthCalc[Rectangle[Some[Double], _ <: Option[Double], _ <: Option[Double]]] {
def length(x: Rectangle[Some[Double], _ <: Option[Double], _ <: Option[Double]]) = x.lengthOpt.get
}
implicit object RectLengthCalcFromWidthAndArea extends LengthCalc[Rectangle[None.type, Some[Double], Some[Double]]] {
def length(x: Rectangle[None.type, Some[Double], Some[Double]]) = (for {
area <- x.areaOpt
width <- x.widthOpt
} yield (area / width)).get
}
implicit object RectWidthFromWidth extends WidthCalc[Rectangle[_ <: Option[Double], Some[Double], _ <: Option[Double]]] {
def width(x: Rectangle[_ <: Option[Double], Some[Double], _ <: Option[Double]]) = x.widthOpt.get
}
implicit object RectWidthFromLengthAndArea extends WidthCalc[Rectangle[Some[Double], None.type, Some[Double]]] {
def width(x: Rectangle[Some[Double], None.type, Some[Double]]) = (for {
area <- x.areaOpt
length <- x.lengthOpt
} yield (area / length)).get
}
implicit object RectAreaFromArea extends AreaCalc[Rectangle[_ <: Option[Double], _ <: Option[Double], Some[Double]]] {
def area(x: Rectangle[_ <: Option[Double], _ <: Option[Double], Some[Double]]) = {
x.areaOpt.get
}
}
implicit object RectAreaFromLengthAndWidth extends AreaCalc[Rectangle[Some[Double], Some[Double], None.type]] {
def area(x: Rectangle[Some[Double], Some[Double], None.type]) = (for {
width <- x.widthOpt
length <- x.lengthOpt
} yield (width * length)).get
}
以下是一些示例调用:
scala> Rectangle(Some(3.),None,Some(4.))
res8: Rectangle[Some[Double],None.type,Some[Double]] = Rectangle(Some(3.0),None,Some(4.0))
scala> res8.width
res9: Double = 1.3333333333333333
scala> Rectangle(Some(3.),None,None)
<console>:25: error: could not find implicit value for parameter widthCalc: WidthCalc[Rectangle[Some[Double],None.type,None.type]]
Rectangle(Some(3.),None,None)
scala> Rectangle(None, Some(8.), Some(64.))
res10: Rectangle[None.type,Some[Double],Some[Double]] = Rectangle(None,Some(8.0),Some(64.0))
scala> res10.length
res11: Double = 8.0