3

我想用对象的类型参数化一个类,以使我的代码更通用。通过这样做,我不需要为所有扩展特定特征的对象实现实现。

我有以下代码可以证明我的目标:

abstract trait Foo {
  def greet
}

object Coo extends Foo {
  def greet = println("Coo!")
}

object Boo extends Foo {
  def greet = println("Boo!")
}

class A[Fooer <: Foo] {
  def moo = asInstanceOf[Fooer].greet()
}

object Main extends App {
  new A[Coo.type].moo() // hopefully prints "Coo!"
}

代码抛出异常:

java.lang.ClassCastException: A cannot be cast to Foo

而且我相信这是因为asInstanceOf似乎this隐含使用的调用。

基本上我的问题是:如何通过参数化在类中按类型获取对象的实例?

4

1 回答 1

4

而且我相信这是因为asInstanceOf似乎this隐含使用的调用。

Scala 中的每个Any都继承了特殊的方法asInstanceOf,等等。我不会说它是隐式发生的——只是你正在调用该类的一个成员方法,它是在自己调用它。就像你写的一样def moo = greet

此外,任何时候使用asInstanceOf,都是在要求编译器对自己撒谎并继续运行。这通常会导致ClassCastException. 在这种情况下,这是因为您试图将实例A转换为Foo. 这永远不会奏效。在非常具体的情况下,您尝试将单例类型传递给Coo.type实例,然后A创建. 虽然还有许多其他原因导致这不起作用,但其中一个原因是您无法创建单例类型的第二个实例——之所以这样称呼它是有原因的。Coo.type

更一般地说,您不知道可以简单地构造给定类型的对象。类型没有构造函数,有。

这可能是作为简化示例的副作用,但您关心的是创建一个A具有某种类型参数的实例,而无需将其传递给实际对象,但您关心的对象已经存在。那么为什么不通过它们呢?

class A[Fooer <: Foo](f: Fooer) {
  def moo = f.greet
}

做到这一点的唯一其他方法是提供Fooer可以构建的证据。但是,没有类型约束来证明某事物是可构造的类。您可以做的是需要一个Class元对象:

class A[Fooer <: Foo](clazz: Class[Fooer]) {
  def moo = clazz.newInstance.greet
}

然而,这很粗糙。首先,它不适用于上面的单例类型(因为它们不能再次构造)。其次,我们只能newInstance在没有构造函数参数提供时调用,并且没有办法强制执行。

所以虽然这会起作用:

scala> class Bar extends Foo { def greet = print("Bar") }

scala> new A(classOf[Bar]).moo
Bar

以下不会:

scala> class Baz(i: Int) extends Foo { def greet = println("Baz") }
defined class Baz

scala> new A(classOf[Baz]).moo
java.lang.InstantiationException: Baz

scala> new A(classOf[Coo.type]).moo
<console>:14: error: class type required but Coo.type found
       new A(classOf[Coo.type]).moo
                        ^

或者,您可以使用允许您一致地构建 的实例的类型类Foo,但这需要为每个子类创建一个。例如:

trait FooBuilder[A] {
    def build(): A
}

implicit object BarBuilder extends FooBuilder[Bar] {
    def build() = new Bar
}

class A[Fooer <: Foo](implicit fb: FooBuilder[Fooer]) {
    def moo = fb.build().greet
}
于 2016-11-07T01:04:45.440 回答