2

我正在使用 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)

我的问题是:

  1. 为什么它通过编译器但在运行时失败?
  2. 在底部添加一行 -val t1 = l.map(item => item.move(item))会使编译器失败,为什么?
4

2 回答 2

3

您的代码null.asInstanceOf[Nothing]compiles 因为Nothing是所有内容的子类,因此符合move. 不用说,它会在运行时抛出异常。

当您尝试编译您给出的第二行时,会在此错误的行中给出一些内容:

<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一个相同类型的对象的方法,你不能这样做:

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!

我对自我类型的了解不够,无法完全确定这种解释,但在我看来,这是一个类级别的限制,而不是实例级别的限制。换句话说,如果你丢失了关于类型参数的信息E,你不能期望编译器知道move参数的特定类型。

对于实例级限制,您有this.type. 如果你定义move为:

def move(a: this.type): Int

您的代码可以编译,但我认为这不是您想要的,因为move它只接受您调用它的同一个实例,这是没用的。

我想不出任何你可以按照你想要的方式强制执行该限制的方式。我建议您尝试使用类型变量(即type T = A在 class 中定义类型变量E)来做到这一点,据我所知,这些变量与实例具有某种程度的绑定。也许您可以更详细地解释您的具体情况?

于 2012-12-30T20:50:42.147 回答
2

回复:第二个问题

问题是move方法参数的类型,如果它以任何方式与子类相关联,我看不到它可以工作,而是:

  • l必须是E-s列表的“某种形式”
  • 那么item将是“某种形式”的E
  • 参数的类型move必须具有相同的类型

这是我可以使它与类型参数一起工作的唯一方法(将类型参数的 btw 名称更改为 X,不要与 A 类的名称混淆,并删除自我类型声明,我认为这与这个问题/讨论无关):

abstract class E[X <: E[X]]
{
    def move (a: E[_]): Int
}

class A extends E[A]
{
    def move(a: E[_]): Int = 1
}
class B extends E[B]
{
    def move(b: E[_]): Int = 2
}

...
{
    val a = new A
    val b = new B
    val l = List (a, b)
    val t = l.map (item => item.move(item))
    ...
}

如果有人可以提供更“类型限制”的解决方案,我真的很想看看。


正如 Rui 所建议的,另一种选择是使用类型成员,如下所示:

abstract class E
{
    type C <: E
    def move (x: E): Int
}

class A extends E
{
    type C = A
    def move(x: E): Int = 1
}

class B extends E
{
    type C = B
    def move(x: E): Int = 2
}

...
{
    val a = new A
    val b = new B
    val l = List (a, b)
    val t = l.map (item => item.move(item))
    ...
}
于 2012-12-30T22:23:18.170 回答