在Scala中的伴随对象的apply方法中实例化一个类的子类型

时间:2014-11-06 04:11:31

标签: scala apply factory-pattern subtype companion-object

我一直在试图使用一个伴侣对象来实例化一个类的子类型。在编译时不知道哪个子类将被实例化。这与从第127页开始的 Programming Scala 中的示例非常相似。我在这里设计了一个例子:

import scala.reflect.runtime.universe._


abstract class Animal
class Dog extends Animal { def bark = "woof" }
class Cat extends Animal { def meow = "meow" }

object Animal {
  def apply(specify: String): Option[Animal] = {
    specify match {
      case "dog" => Some(new Dog)
      case "cat" => Some(new Cat)
      case _     => None
    }
  }
}

object Test extends App {
  def getType[T: TypeTag](obj: T) = typeOf[T]

  var dog = Animal("dog")
  println(getType(dog))
}

此程序打印出scala.Option[Animal]。我希望它打印出scala.Option[Dog]。此外,如果我尝试将行println(dog.bark)添加到Test对象的末尾,则无法编译。这根本不可能吗?

我一直在研究Scala反射文档,但它似乎非常密集和困难。此外,这似乎正是 Programming Scala 示例的工作方式,所以我无法想象我在这里做错了什么。


编辑:此版本没有反射,只是由于类型错误而引发编译时错误。

abstract class Animal
class Dog extends Animal { def bark = "woof" }
class Cat extends Animal { def meow = "meow" }

object Animal {
  def apply(specify: String): Option[Animal] = {
    specify match {
      case "dog" => Some(new Dog)
      case "cat" => Some(new Cat)
      case _     => None
    }
  }
}

object Test extends App {
  var dog = Animal("dog")
  println(dog.get.bark)
}

// $ scalac test.scala
// test.scala:17: error: value bark is not a member of Animal
//   println(dog.get.bark)
//                   ^
// one error found

编辑:显然这需要模式匹配才能解决问题。这是一个有点简化的工作示例。

abstract class Animal
class Dog extends Animal { def bark = "woof" }
class Cat extends Animal { def meow = "meow" }

object Animal {
  def apply(specify: String): Animal = {
    specify match {
      case "dog" => new Dog
      case "cat" => new Cat
    }
  }
}

object Test extends App {
  val dog = Animal("dog")
  dog match {
    case d: Dog => println(d.bark)
    case _      =>
  }
}

1 个答案:

答案 0 :(得分:1)

这是不可能的。

在第二个示例中,Animal.apply始终返回Option[Animal],因为这是其类型签名。

你的object Test真的在说,扩展了一点:

object Test extends App {
  var dog: Option[Animal] = Animal.apply("dog")
  println(dog.get.bark)
}

编译器可能能够在编译时告诉它可能是Option[Dog],但语言的语义不允许:语法要能够封装这些知识,必须要复杂得多。