2012-02-12 10 views
4

我很難從C++ /模板轉換到Scala。我習慣於能夠對我想要的模板參數T使用任何操作,只要我用來實例化T的任何操作都支持這些操作(基本上是編譯時Duck打字)。我無法在Scala中找到相應的成語,這將允許我使用單個類型參數定義抽象類,並且需要特定類型T的接口。在斯卡拉,我如何告訴抽象基類,類型參數T支持從Int(或浮點數,或...)的隱式轉換?

我幾乎可以工作,但我無法弄清楚如何告訴抽象類(紋理[T <:Summable [T]])T支持來自Int的轉換/構造。如何將隱式轉換添加到Summable特性中,以便Texture知道T支持轉換?

trait Summable[T] { 
    def += (v : T) : Unit 
    def -= (v : T) : Unit 
} 

object Int4 { implicit def int2Int4(i : Int) = new Int4(i, i, i, i) } 

class Int4 (var x : Int, var y : Int, var z : Int, var w : Int) extends Summable[Int4] { 
    def this (v : Int) = this(v, v, v, v) 
    def += (v : Int4) : Unit = { x += v.x; y += v.y; z += v.z; w += v.w } 
    def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w } 
} 

abstract class Texture[Texel <: Summable[Texel]] { 
    var counter : Texel 
    def accumulate(v : Texel) : Unit = { counter += v } 
    def decrement() : Unit = { counter -= 1 } //< COMPILE ERROR HERE, fails to find implicit 
} 

class Int4Target extends Texture[Int4] { 
    var counter : Int4 = new Int4(0, 1, 2, 3) 
} 

回答

2

不可能在階爲要求的隱式轉換爲一個性狀的一個 類型參數存在。這有一個很好的理由。假設我們定義了一個 特點,如:

trait ATrait[T <% Int] { 
    def method(v: T) { println(v: Int) } 
} 

然後用它製成的情況下,在兩個地方:

package place1 { 
    implicit def strToInt(s: String) = 5 
    val inst = new ATrait[String] 
} 

package place2 { 
    implicit def strToInt(s: String) = 6 
    val inst = new ATrait[String] 
} 

然後使用這些實例,如:

val a = if (someTest) place1 else place2 
a.method("Hello") 

若本打印56?也就是說,它應該使用哪種隱式轉換? 隱含必須在編譯時找到,但是您不知道創建對象時存在哪種隱式轉換。

換句話說,暗示是使用它們的範圍提供的,而不是 所使用的對象;後者將是不可能的。

所以,關於你的問題。除了使用隱式的,你可以使用一個普通的 成員:

trait Summable[T] { 
    def -= (v: T): Unit 
    def -= (v: Int) { this -= (encode(v)) } 

    def encode(i: Int): T 
} 

class Int4 (var x: Int, var y: Int, var z: Int, var w: Int) extends Summable[Int4] { 
    def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w } 

    def encode(i: Int) = Int4.int2Int4(i) 
} 

現在decrement方法正確編譯。

另一種表達方式是,不要將implicits看作屬於 的屬性(即,「可以從Int中隱式轉換」不是 Int4的屬性)。它們是值,可以使用類型來識別。

希望這會有所幫助。

+2

我不追隨你反對暗示特質。對性狀的暗示是不可能的,因爲性狀不支持construtors。類可以並經常有暗示,包括像這裏所要求的那樣 - 爲什麼類可以擁有它而不是特性(除了缺少構造函數)? – 2012-02-12 07:07:24

+0

@Daniel就是這樣。因爲類有一個構造函數,所以隱式變成了一個參數,所以變成了一個成員。有了特質,所有成員都被明確地聲明。基本上我的觀點是,如果沒有成員,你不能攜帶隱含的價值,所以它不能作爲[T <%S]出現在簽名中。即使是一個帶有隱含參數的類,使用該類實例的人也不會自動在範圍內使用它。 – Owen 2012-02-12 19:04:54

4

你可以這樣定義

abstract class Texture[Texel <: Summable[Texel]](implicit int2Texel: Int => Texel) { 
//... 

這實質上告訴編譯器,以構建的Texture一個實例,還必須有從IntTexel的隱式轉換功能的隱式構造函數參數。假設你在範圍的某個地方定義了這樣一個函數(你這樣做),你不應該再得到一個編譯錯誤。

編輯2:好吧,我最初誤讀你的代碼,你實際上只需要一個來自Int => Texel的隱式參數。您的代碼通過上述修改爲我編譯。

編輯:你真的需要2個轉換功能,一個從Texel => Int,另一名來自Int => Texel,以便正確地重新分配VAR

3

C++模板和Scala中的任何一個根本區別是,C++模板編譯每次使用 - 也就是說,如果您使用intdouble的模板,則會編譯兩個不同的類,並且只有在某些代碼實際使用它時纔會編譯它們。

另一方面,斯卡拉有單獨的彙編。由於JVM的限制,它不如Java的好,但仍遵循基本原則。所以,如果有一個類型參數,它仍然在聲明中編譯,並且只存在一個這樣的類。該編譯代碼必須支持所有可能的參數,而不是可以調用的參數,這使得與模板相比具有不同的限制。

在特徵和隱式轉換的問題上,特徵不支持參數,而隱式轉換(視圖邊界)是參數。相反,使用一個類。