如何使用对象类型参数化 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
}
我想用对象类型参数化 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 中按类型获取我的对象的实例?
Scala 中的And I believe it's because of the
asInstanceOf
call which is seemingly usingthis
implicitly.
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
}