简体   繁体   English

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

[英]Instantiating a class's subtype in the companion object's apply method in Scala

I've been knocking myself out trying to use a companion object to instantiate one of a class's subtypes. 我一直在努力尝试使用同伴对象来实例化类的子类型之一。 It's not known at compile time which subclass will be instantiated. 在编译时尚不知道哪个子类将被实例化。 This is remarkably similar to an example found in Programming Scala starting on page 127. I've contrived an example here: 这与从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))
}

This program prints out scala.Option[Animal] . 该程序打印出scala.Option[Animal] I'd expect it to print out scala.Option[Dog] . 我希望它能打印出scala.Option[Dog] Furthermore, if I attempt to add the line println(dog.bark) to the end of the Test object, it fails to compile. 此外,如果我尝试将行println(dog.bark)添加到Test对象的末尾,它将无法编译。 Is this simply impossible? 这根本不可能吗?

I've been poring over Scala reflection documentation, but it seems very dense and difficult. 我一直在研究Scala反射文档,但是它看起来非常密集且困难。 Furthermore, this seems to be exactly how the Programming Scala example works, so I can't imagine what I've done wrong here. 此外,这似乎正是Programming Scala示例的工作原理,所以我无法想象自己在这里做错了什么。


EDIT: This version doesn't have reflection and simply throws a compile-time error due to the wrong type. 编辑:此版本没有反射,只是由于类型错误而引发了编译时错误。

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

EDIT: Evidently this requires pattern matching to work out. 编辑:显然,这需要模式匹配才能解决。 Here is a working example, somewhat simplified. 这是一个工作示例,有些简化了。

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 _      =>
  }
}

It is impossible. 是不可能的。

In the second example, Animal.apply always returns Option[Animal] , because that is its type signature. 在第二个示例中, Animal.apply始终返回Option[Animal] ,因为这是其类型签名。

Your object Test is really saying, expanded out a bit: 您的object Test确实在说,扩展了一点:

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

The compiler might be able to tell at compile time that it could be an Option[Dog] , but the language's semantics don't allow for that: the grammar would have to be much more complex to be able to encapsulate that knowledge. 编译器也许可以在编译时告诉它可能是Option[Dog] ,但是语言的语义不允许这样做:语法必须更加复杂才能封装这些知识。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM