Scala 中的 F 有界多态性 [英] F-Bounded Polymorphism in Scala
问题描述
我使用的是 Scala 2.10-RC5
I am using Scala 2.10-RC5
这是我的代码:
object Fbound {
abstract class E[A <: E[A]] {
self: A =>
def move(a: A): Int
}
class A extends E[A] {
override def toString = "A"
def move(a: A) = 1
}
class B extends E[B] {
override def toString = "B"
def move(b: B) = 2
}
def main(args: Array[String]): Unit = {
val a = new A
val b = new B
val l = List(a, b)
val t = l.map(item => item.move(null.asInstanceOf[Nothing]))
println(t)
}
}
运行程序时出现异常:
Exception in thread "main" java.lang.NullPointerException
at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.immutable.List.foreach(List.scala:309)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.AbstractTraversable.map(Traversable.scala:105)
at fb.Fbound$.main(Fbound.scala:20)
at fb.Fbound.main(Fbound.scala)
我的问题是:
- 为什么它通过编译器但在运行时失败?
- 在底部添加一行 -
val t1 = l.map(item => item.move(item))
会使编译器失败,为什么?
- Why it pass the compiler but fail at runtime?
- Adding a line at the bottom -
val t1 = l.map(item => item.move(item))
will fail the compiler, why?
推荐答案
您的带有 null.asInstanceOf[Nothing]
的代码可以编译,因为 Nothing
是所有内容的子类,并且,因此,符合 move
所需的类型.不用说,它会在运行时抛出异常.
Your code with null.asInstanceOf[Nothing]
compiles because Nothing
is a subclass of everything and, as such, complies with the required type for move
. Needless to say, it will throw an exception at runtime.
当你尝试编译你给出的第二行时,你会在这个错误的行中得到一些东西:
When you try to compile the second line you gave, you are given something in the lines of this error:
<console>:19: error: type mismatch;
found : E[_6(in value $anonfun)] where type _6(in value $anonfun) >: B with A
<: E[_ >: B with A <: Object]
required: <root>._6
当您将两个实例放在同一个 List
中时,您丢失了有关它们元素类型的重要信息.编译器无法确保 T >: B with A <: E[_ >: B with A]
类型的元素可以传递给 move
> 同类型对象的方法,同样的方法你做不到:
When you put the two instances in the same List
, you have lost important information about the type of their elements. The compiler can't ensure that an element of type T >: B with A <: E[_ >: B with A]
can be passed to the move
method of an object of the same type, the same way that you can't do:
val c: E[_ >: B with A] = new A
val d: E[_ >: B with A] = new B
c.move(d) // note: the _ in c and d declarations are different types!
我对 self-types 的了解不够,无法完全确定这种解释,但在我看来,这是一个类级别的限制,而不是一个实例级别的限制.换句话说,如果你在 E
中丢失了关于类型参数的信息,你不能指望编译器知道 move
参数的特定类型.
I don't know enough about self-types to be completely sure of this explanation, but it seems to me that it is a class-level restriction, and not an instance-level one. In other words, if you lose the information about the type parameter in E
, you can't expect the compiler to know about the move
argument particular type.
对于实例级别的限制,您有 this.type
.如果您将 move
定义为:
For instance-level restrictions, you have this.type
. If you define move
as:
def move(a: this.type): Int
您的代码可以编译,但我认为这不是您想要的,因为 move
将只接受您正在调用它的 相同实例,这是无用的.
Your code compiles, but I don't think it is what you want, as move
will accept only the same instance you are calling it on, which is useless.
我想不出您可以按照自己的意愿强制执行该限制的任何方式.我建议您尝试使用类型变量(即在 E
类中定义类型变量 type T = A
)来做到这一点,据我所知,这些变量在某种程度上绑定到实例.也许您可以更详细地说明您的具体情况?
I can't think of any way you may enforce that restriction the way you want. I suggest you try to do that with type variables (i.e. defining a type variable type T = A
in class E
), which have, as far as I know, some degree of binding to instances. Perhaps you can explain in more detail your specific situation?
这篇关于Scala 中的 F 有界多态性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!