2013-03-30 255 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