如何使用对象类型参数化 class 以在 Scala 中获取它的实例?

How can I parameterize a class with an object's type to get an instance of it in Scala?

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

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

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

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

And I believe it's because of the asInstanceOf call which is seemingly using this implicitly.

Scala 中的

Every class 扩展了 Any,它继承了特殊方法 asInstanceOf,等等。我不会说它正在 隐含地发生 ——只是你正在调用那个 class 的成员方法,它在自身上调用它。就像你写 def moo = greet.

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

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

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

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

唯一的其他方法是提供证据证明 Fooer 可以构造。但是,没有 type 约束来证明某物是可构造的 class。你可以做的是需要一个 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
                        ^

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

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
}