Scala工厂模式返回不可用的抽象类型

时间:2013-10-28 08:56:26

标签: scala factory-method

请告诉我如何使下面的代码按预期工作。问题是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

3 个答案:

答案 0 :(得分:4)

您应该为apply的每个子类而不是Animal特征使用Animal方法创建伴随对象。此外,像使用name一样使用可变字段被认为是一种不好的做法。

答案 1 :(得分:0)

您可以这样做,而无需更改任何其他内容:

val d = Animal("dog").asInstanceOf[Dog]     //> d  : Test.Dog = Test$$anonfun$main$1$Dog$1@1030dda
name(d, "fred")                                   //> Test$$anonfun$main$1$Dog$1@1030dda's name is: fred

但是,我认为这不是一个好主意......

答案 2 :(得分:0)

我不想听起来粗鲁,但编译器认为dAnimal是正确的,因为这是Animal.apply方法返回的内容。

正如已经指出的那样,您可以使用显式强制类型强制执行d类型,但它不会是类型安全的。它将利用您对作为程序员的方法实现的了解,随着代码库的增长最终成为错误的来源,并且您可能以意想不到的方式更改以前的代码。

如果需要调用Pet方法,那么最好使用创建Pet对象的工厂方法,或者至少在进行类型转换之前检查对象类型,使用

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

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

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