6

我有一個設計模式,其中有一個對象生成器(MorselGenerator及其子),其任何實例總是生成相同的確切類型的對象少量食物和它的孩子),但類型檢查員不會讓我對這些生成的物體中的兩個或多個進行任何操作,相信它們可能會有所不同。斯卡拉類型推斷未能注意到這些類型是相同的,無論它們是什麼

我怎樣才能讓這個過去的類型檢查?

trait Morsel 
{ 
    type M <: Morsel 
    def calories : Float 
    def + (v : M) : M 
} 

trait MorselGenerator 
{ 
    type Mg <: Morsel 
    def generateMorsel : Mg 
} 

class HotDog(c : Float, l : Float, w : Float) extends Morsel 
{ 
    type M = HotDog 
    val calories : Float = c 
    val length : Float = l  
    val width : Float = w 
    def + (v : HotDog) : HotDog = new HotDog(v.calories + calories, v.length + length, v.width + width) 
} 

class HotDogGenerator extends MorselGenerator 
{ 
    type Mg = HotDog 
    def generateMorsel : HotDog = new HotDog(500.0f, 3.14159f, 445.1f) 
} 

object Factory 
{ 
    def main (args : Array[String]) 
    { 
     val hdGen = new HotDogGenerator() 
     println(eatTwo(hdGen)) 
    } 

    def eatTwo (mGen : MorselGenerator) 
    { 
     val v0 : mGen.Mg = mGen.generateMorsel 
     val v1 : mGen.Mg = mGen.generateMorsel 
     v0 + v1       /// ERROR HERE 
    } 
} 

編譯器生成以下編譯錯誤

Generator.scala:43: error: type mismatch; 
found : v1.type (with underlying type mGen.Mg) 
required: v0.M 
     v0 + v1       /// ERROR HERE 
     ^one error found 


更新

這裏是C++代碼,那就是我想要做的更多或更少的等價物。請注意,eatTwo函數是完全多態的,並且沒有提及特定派生類型的Morsel或MorselGenerator。

#include <stdlib.h> 
#include <stdio.h> 

template <class M> class Morsel 
{ 
public: 
    Morsel(float c) : calories(c) {} 
    float calories; 
    virtual M operator + (const M& rhs) const = 0; 
}; 

template <class M> class MorselGenerator 
{ 
public: 
    virtual M * generateMorsel() const = 0; 
}; 

class HotDog : public Morsel<HotDog> 
{ 
public: 
    HotDog(float c, float l, float w) : Morsel<HotDog>(c), length(l), width(w) {} 
    float length, width; 

    HotDog operator + (const HotDog& rhs) const 
    { return HotDog(calories+rhs.calories, length+rhs.length, width+rhs.width); } 
}; 

class HotDogGenerator : public MorselGenerator<HotDog> 
{ 
    HotDog * generateMorsel() const { return new HotDog(500.0f, 3.14159f, 445.1f); } 
}; 

/////////////////////////////////////////////// 

template <class MorselType> float eatTwo (const MorselGenerator<MorselType>& mGen) 
{ 
    MorselType * m0 = mGen.generateMorsel(); 
    MorselType * m1 = mGen.generateMorsel(); 
    float sum = ((*m0) + (*m1)).calories; 
    delete m0; delete m1; 
    return sum; 
} 

int main() 
{ 
    MorselGenerator<HotDog> * morselStream = new HotDogGenerator(); 
    printf("Calories Ingested: %.2f\n", eatTwo(*morselStream)); 
    delete morselStream; 
} 
+0

也許這將有助於:http://stackoverflow.com/questions/9198562/scala-self-type-and-this-type-in​​-collections-issue – tuxSlayer 2012-02-22 20:01:46

回答

2

這就是成員類型如何在Scala中工作的:它們只在外部對象(編譯器已知)相同時才被認爲是相等的。一種選擇是使用類型參數代替:

trait Morsel[M <: Morsel] 
{ 
    def calories : Float 
    def + (v : M) : M 
} 

trait MorselGenerator[Mg <: Morsel] 
{ 
    def generateMorsel : Mg 
} 

... 
+0

嗯,這似乎並沒有工作,實際上。首先,我們必須定義這些特徵:'特質Morsel [Mg <:Morsel [Mg]]'等等,看起來奇怪的是圓形。通過這個設計,編譯器比以前更加困惑,當我嘗試添加兩個Morsels(HotDogs)時。 – Fooberman 2012-02-18 21:42:36

+1

這並不比'class Hotdog ... {type M = HotDog ...}'更具循環性。請參閱http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – 2012-02-19 15:57:30

4

錯誤是有道理的,因爲在編譯失敗的方法,編譯器不能保證您不添加到冰淇淋熱狗。

在熱狗

的+方法有助於突出的問題,其實你還沒有覆蓋的方法,而你已經添加了一個新問題:

def + (v : HotDog) : HotDog = new HotDog(v.calories + calories, v.length + length, v.width + width) 

你需要明確的類型被添加到有與「this」相同的類型。

定義小肉塊這樣,問題幾乎是解決:

trait Morsel { 
    def calories : Float 
    def + (v : Morsel) : Morsel 
} 

最後一部分是覆蓋+方法正確:

override def + (v : Morsel): Morsel = v match { 
    case hd: HotDog => new HotDog(hd.calories + calories, hd.length + length, hd.width + width) 
    case x => throw new IllegalArgumentException("eurgh!") 
} 

我不知道你是否能得到編譯器阻止添加icecream和熱狗,使用您提供的表單中的代碼。

0

一個可能的解決方案(我已經更換了+add這裏從+(String, String)望而卻步,最終,+是確定):

trait Morsel[M <: Morsel[M]] { /// this 
    this: M =>      /// and this make the trick 
    def calories : Float 
    def add(v : M) : M 
} 

trait MorselGenerator[Mg <: Morsel[Mg]] 
{ 
    def generateMorsel : Mg 
} 

class HotDog(c : Float, l : Float, w : Float) extends Morsel[HotDog] 
{ 
    val calories : Float = c 
    val length : Float = l  
    val width : Float = w 
    override def add (v : HotDog) : HotDog = new HotDog(v.calories + calories, v.length + length, v.width + width) 
} 

class HotDogGenerator extends MorselGenerator[HotDog] 
{ 
    def generateMorsel : HotDog = new HotDog(500.0f, 3.14159f, 445.1f) 
} 

object Factory extends App 
{ 
    def eatTwo[M <: Morsel[M]](mGen : MorselGenerator[M]) = { 
    val v0 = mGen.generateMorsel 
    val v1 = mGen.generateMorsel 
    v0 add v1  
    } 

    val hdGen = new HotDogGenerator() 
    println(eatTwo(hdGen)) 
} 
0

和輕微的另一種變體:

trait MorselGenerator { 
    type M <: Morsel 

    trait Morsel { this: M => 
    def calories : Float 
    def add (v : M) : M 
    }  

    def generateMorsel : M 
} 

class HotDogGenerator extends MorselGenerator 
{ 
    type M = HotDog 

    class HotDog(c : Float, l : Float, w : Float) extends Morsel { 
    val calories : Float = c 
    val length : Float = l  
    val width : Float = w 
    def add (v : HotDog) : HotDog = new HotDog(v.calories + calories, v.length + length, v.width + width) 
    } 

    def generateMorsel: HotDog = new HotDog(500.0f, 3.14159f, 445.1f) 
} 

object Factory extends App 
{ 
    val hdGen = new HotDogGenerator() 

    hdGen.generateMorsel add hdGen.generateMorsel add hdGen.generateMorsel 

    produceDouble(hdGen) 

    def produceDouble(gen: MorselGenerator): MorselGenerator#Morsel = { 
    gen.generateMorsel add gen.generateMorsel 
    } 
} 

可能不太有用,但它可能會顯示問題出在哪裏。 Scala具有「路徑依賴」類型,因此,即使obj1.type == obj2.type,obj1.Type和obj2.Type也是不同的類型。