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



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

下面的代码演示了我的目标:
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

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

我认为这是因为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
}

相关内容

  • 没有找到相关文章

最新更新