2013-10-28 22 views
-1

請讓我知道如何使下面的一段代碼按預期工作。問題是Scala編譯器不明白我的工廠正在返回一個具體的類,所以我的對象不能在以後使用。 TypeTags或類型參數可以幫助嗎?或者我需要以其他方式重構代碼?我(顯然)是Scala的新手。斯卡拉工廠模式返回不可用的抽象類型

trait Animal 
trait DomesticatedAnimal extends Animal 
trait Pet extends DomesticatedAnimal {var name: String = _} 
class Wolf extends Animal 
class Cow extends DomesticatedAnimal 
class Dog extends Pet 

object Animal { 
    def apply(aType: String) = { 
     aType match { 
      case "wolf" => new Wolf 
      case "cow" => new Cow 
      case "dog" => new Dog 
     } 
    } 
} 

def name(a: Pet, name: String) { 
    a.name = name 
    println(a +"'s name is: " + a.name) 
}            

val d = Animal("dog")              
name(d, "fred") 

的最後一行代碼,因爲編譯器認爲dAnimal,而不是一個Dog失敗。

+0

編譯器應該如何推斷'd'是'Dog'? – Giorgio

+0

@giorgio,這就是我想要理解的。我看到了這個問題,但我不知道如何解決它,除了爲每個類添加更多的代碼。 – GGGforce

+0

'd'不能是'Pet'類型(由函數'name()'所要求的),因爲'Animal.apply()'返回一個'Animal'並不是所有的動物都是'Pet'。例如,「狼」不是「寵物」。所以編譯器是正確的抱怨你的類型:如果** all **'Animal.apply()'的可能結果都是'Pet'類型,''d''''可以具有'Pet'類型。 – Giorgio

回答

4

您應該創建伴隨對象特性Animal的每個子類的apply方法。另外,像使用name那樣使用可變字段被認爲是不好的做法。

0

你可以做的是,在不改變任何東西:

val d = Animal("dog").asInstanceOf[Dog]  //> d : Test.Dog = [email protected] 
name(d, "fred")         //> Test$[email protected]'s name is: fred 

但是,我不認爲這是一個很好的主意...

0

我不想健全粗魯,但編譯器是正確的假設dAnimal,因爲這是Animal.apply方法返回的內容。

正如已經指出的那樣,你可以用顯式強制類型強制d類型,但它不會是類型安全的。它會利用您作爲程序員的方法實現的知識,隨着代碼庫的增長,您最終會成爲bug的來源,並且您可能以意想不到的方式更改以前的代碼。

如果您需要調用一個Pet方法,那麼你會更好地利用創建Pet對象,或至少做類型轉換之前檢查對象類型的工廠方法,使用

if (d.isInstanceOf[Pet]) name(d.asInstanceOf[Pet], "Fred") 

或者更好的是,使用模式匹配

val d = Animal("dog") 
d match { 
    case p: Pet => name(p, "fred") 
    case _ => 
}