2013-03-30 253 views
0

我试图创建一个库,可以将距离从一个单位转换为另一个。理想情况下,我可以在一个单元中指定距离,当传递给需要不同单元的方法时,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() 

使用它,而无需显式声明的类型参数似乎让斯卡拉使用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  

继编译器的建议,导致富返回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

首先:为什么'Distance'类具有类型参数'A'开头?你似乎没有在任何地方使用它...... – ghik

+0

@ghik哎呀,我在我的实际代码中使用A,并且在构建这个例子时一定会意外地将它拿出来。现在修复它 – Matt

回答

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)) 

或类似的东西。 (正如你现在定义的那样,顺便说一下,转换是倒退的。)

+0

谢谢,这是固定的!不幸的是,从真实的破坏代码构造示例时,不会使用'A'是一个错字。请你能解释一下你的意思,“现在你定义它,转换是顺带的,顺便说一句。”? – Matt

+0

@Matt - 10英寸不是3.93厘米,但这是自然转换的结果。 –

+0

啊我明白你的意思了。谢谢,我想我一直在盯着这台电脑太久了! – Matt