我正在尝试创建一个可以将距离从一个单位转换为另一个单位的库。理想情况下,我可以在一个单元中指定距离,并且当传递给需要不同单元的方法时,让scala编译器自动转换它。这就是我到目前为止所做的:
abstract class BaseUnit(scale: Option[Double] = None) {
def unit: String
def scalingFactor: Double = scale match {
case Some(factor) => factor
case None => 1.0
}
}
object Cm {
implicit def inch2cm(inch: Inch):Cm = new Cm(Some(0.393 * inch.scalingFactor))
}
class Cm(scale: Option[Double] = None) extends BaseUnit(scale) {
def unit: String = "cm"
}
object Inch {
implicit def cm2inch(cm: Cm):Inch = new Inch(Some(2.54 * cm.scalingFactor))
}
class Inch(scale: Option[Double] = None) extends BaseUnit(scale) {
def unit: String = "inch"
}
class Distance[A <: BaseUnit](val scalar: Double, val unit: A) {
override def toString: String = (scalar*unit.scalingFactor)+unit.unit
}
def foo(x: Distance[Cm], y: Distance[Cm]): String = x.toString()+","+y.toString()
在没有明确说明类型参数的情况下使用它似乎使Scala使用Nothing
类型:
val a = new Distance(10, new Inch)
println(foo(a, a))
> scala test.scala
found : this.Distance[Nothing]
required: this.Distance[this.Cm]
Note: Nothing <: this.Cm, but class Distance is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
println(foo(a, a))
^
one error found
根据编译器的建议,foo返回10.0inch,10.0inch
而不是预期的3.93cm,3.93cm
。
如果我明确指定了类型,编译器会选择差异,但仍然不会隐式地将其转换为另一个。
val a = new Distance[Inch](10, new Inch)
println(foo(a, a))
// found : this.Distance[this.Inch]
// required: this.Distance[this.Cm]
// println(foo(a, a))
// ^
// one error found
我做错了什么,或者编译器是否不允许使用隐式转换?
答案 0 :(得分:1)
你只需要
class Distance[A <: BaseUnit](val scalar: Double, val unit: A) { ... }
因此编译器有理由不使A
过于具体。否则,可以自由选择Nothing
,因为它与你正在做的任何事情无关。
此外,您知道如何在单位之间进行转换,但您还没有教它如何在距离之间进行转换。你可以:
implicit def convertDist[A <: BaseUnit, B <: BaseUnit](da: Distance[A])(implicit a2b: (A => B)): Distance[B] = new Distance[B](da.scalar, a2b(da.unit))
或类似的东西。 (正如您现在定义的那样,转换是偶然的。)