我想用对象的类型参数化一个类,以使我的代码更通用。通过这样做,我不需要为所有扩展某个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
}