我正在玩一些隐式(我目前正在学习Scala),并编写了一个简单的类来表示温度,并进行了比例转换。它使用'TempOps'类来提供对数字的隐式操作,因此我可以编写如下内容:
(5 Celsius) asFahrenheight
因为我希望它适用于任何数字类型(至少是内置类型),所以我使用隐式numeric类型类抽象TempOps类。代码如下:
package exercises.temperature
/**
* Created by steve on 9/16/2015.
*/
sealed case class Temperature(kelvin: Double) {
import Temperature._
override def toString():String = s"$kelvin Kelvin"
def asKelvin = kelvin
def asFahrenheight = ctof(ktoc(kelvin))
def asCelsius = ktoc(kelvin)
def asK = asKelvin
def asF = asFahrenheight
def asC = asCelsius
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N]) {
implicit val Fahrenheit = Temperature.Fahrenheit(n.toDouble(t))
implicit val Celsius = Temperature.Celsius(n.toDouble(t))
implicit val Kelvin = Temperature(n.toDouble(t))
}
object Temperature {
private val absoluteZeroC = -273.15
private def ftoc(f: Double) = (f-32)*5/9
private def ctok(c: Double) = c - absoluteZeroC
private[temperature] def ktoc(k: Double) = k + absoluteZeroC
private[temperature] def ctof(c: Double) = c*9/5 + 32
private[temperature] def Fahrenheit(f: Double) = Temperature(ctok(ftoc(f)))
private[temperature] def Celsius(c: Double) = Temperature(ctok(c))
implicit def toTempOps(n: Int) = TempOps(n)
}
这可以工作,但是假设我想保留原始的数字类型,以便在下面的结果中仍然是Int而不是Double(显然可以舍入一些):
val originalTempValue: Int = 5 // Explicitly an Int
val converted = (originalTempValue Celsius) asFahrenheit
'已转换'现在将是Double类型。我如何修改tempop以"保留"隐式使用的数字类型,以便在上面的"转换"将最终成为Int?
您需要从值的来源跟踪原始T,然后定义从double到该T的转换
例如:trait ConvertTemp[T] extends (Double ⇒ T)
object ConvertTemp {
def apply[T](f: Double ⇒ T) = new ConvertTemp[T] { override def apply(v: Double) = f(v) }
implicit val convertToInt = apply(Math round _)
implicit val convertToDouble = apply(identity)
}
sealed case class Temperature[T](kelvin: Double)(implicit convert: ConvertTemp[T]) {
import Temperature._
override def toString(): String = s"$kelvin Kelvin"
def asKelvin = convert(kelvin)
def asFahrenheight = ctof(ktoc(kelvin))
def asCelsius = ktoc(kelvin)
def asK = asKelvin
def asF = asFahrenheight
def asC = asCelsius
}
object Temperature {
private val absoluteZeroC = -273.15
private def ftoc(f: Double) = (f - 32) * 5 / 9
private def ctok(c: Double) = c - absoluteZeroC
private[temperature] def ktoc(k: Double) = k + absoluteZeroC
private[temperature] def ctof(c: Double) = c * 9 / 5 + 32
private[temperature] def Fahrenheit[T](f: Double)(implicit convert: ConvertTemp[T]) = Temperature(ctok(ftoc(f)))
private[temperature] def Celsius[T](c: Double)(implicit convert: ConvertTemp[T]) = Temperature(ctok(c))
implicit def toTempOps(n: Int) = TempOps(n)
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N]) {
implicit val Fahrenheit = Temperature.Fahrenheit(n.toDouble(t))
implicit val Celsius = Temperature.Celsius(n.toDouble(t))
implicit val Kelvin = Temperature(n.toDouble(t))
}
根据上面emilianogc的建议,我得出了下面的解决方案。
package exercises.temperature
/**
* Created by steve on 9/16/2015.
*/
trait ConvertTemp[T] extends (Double => T)
object ConvertTemp {
def apply[T](f: Double => T) = new ConvertTemp[T] { override def apply(v: Double) = f(v) }
implicit val convertToInt = apply(x=>Math.round(x).toInt)
implicit val convertToLong = apply(Math.round _)
implicit val convertToDouble = apply(identity)
}
sealed case class Temperature[N](private val kelvin: Double)(convert: ConvertTemp[N]) {
import Temperature._
override def toString():String = s"$kelvin Kelvin"
def asKelvin = convert(kelvin)
def asFahrenheight = convert(ctof(ktoc(kelvin)))
def asCelsius = convert(ktoc(kelvin))
def asK = asKelvin
def asF = asFahrenheight
def asC = asCelsius
}
sealed case class TempOps[N](t: N)(implicit n: Numeric[N], convert: ConvertTemp[N]) {
implicit val Fahrenheit = Temperature.Fahrenheit(t)(n,convert)
implicit val Celsius = Temperature.Celsius(t)(n,convert)
implicit val Kelvin = Temperature.Kelvin(t)(n,convert)
}
object Temperature {
private val absoluteZeroC = -273.15
private def ftoc(f: Double) = (f-32)*5/9
private def ctok(c: Double) = c - absoluteZeroC
private[temperature] def ktoc(k: Double) = k + absoluteZeroC
private[temperature] def ctof(c: Double) = c*9/5 + 32
private[temperature] def Fahrenheit[N](f: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(ctok(ftoc(n.toDouble(f))))(convert)
private[temperature] def Celsius[N](c: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(ctok(n.toDouble(c)))(convert)
private[temperature] def Kelvin[N](c: N)(n: Numeric[N], convert: ConvertTemp[N]) = Temperature(n.toDouble(c))(convert)
implicit def toTempOps(t: Int) = TempOps(t)
implicit def toTempOps(t: Long) = TempOps(t)
implicit def toTempOps(t: Double) = TempOps(t)
}
我仍然不满意的是,在传递数字类类型和转换器时,在它不能隐式的地方似乎有相当多的"样板语言"(因为我们已经在处理只能通过其他隐式推断的泛型类型,我认为)。如果有人能够找到一种方法,使它在这方面不那么混乱,并且仍然编译(我最初期望的一些显式类型类可以推断,但如果我试图在上面的隐式代码中做更多的事情,编译器似乎不想玩球)