简体   繁体   English

Scala中的F界多态性

[英]F-Bounded Polymorphism in Scala

I am using Scala 2.10-RC5 我正在使用Scala 2.10-RC5

Here is my code: 这是我的代码:

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)
    }
}

when run the program, exception occurs: 运行程序时,发生异常:

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)

my question is: 我的问题是:

  1. Why it pass the compiler but fail at runtime? 为什么通过编译器但在运行时失败?
  2. Adding a line at the bottom - val t1 = l.map(item => item.move(item)) will fail the compiler, why? 在底部添加一行val t1 = l.map(item => item.move(item))将使编译器失败,为什么?

Your code with null.asInstanceOf[Nothing] compiles because Nothing is a subclass of everything and, as such, complies with the required type for move . 使用null.asInstanceOf[Nothing]代码将进行编译,因为Nothing是所有内容的子类,因此,它符合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

When you put the two instances in the same List , you have lost important information about the type of their elements. 将两个实例放在同一List ,您会丢失有关其元素类型的重要信息。 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: 编译器无法确保将类型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!

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. 换句话说,如果您丢失了E有关类型参数的信息,则不能指望编译器知道特定于move参数的类型。

For instance-level restrictions, you have this.type . 对于实例级别的限制,您具有this.type If you define move as: 如果将move定义为:

def move(a: this.type): Int

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. 您的代码可以编译,但是我认为这不是您想要的,因为move将仅接受您调用它的同一实例 ,这是没有用的。

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 (ie defining a type variable type T = A in class E ), which have, as far as I know, some degree of binding to instances. 我建议您尝试使用类型变量(即在类E定义类型变量type T = A的类型)来做到这一点,据我所知,它们对实例具有某种程度的绑定。 Perhaps you can explain in more detail your specific situation? 也许您可以更详细地说明您的具体情况?

Re: second question 回复:第二个问题

The problem is type of the argument of move method, I can't see it being possible to make it work if it is in any way tied to subclass(es), instead: 问题是move方法的参数类型,如果它以任何方式绑定到子类,我看不到有可能使其起作用,而是:

  • l has to be 'some form' of list of E -s l必须是E -s列表的“某种形式”
  • so then item will be 'some form' of E 因此item将是E “某种形式”
  • type of argument to move has to have the same type move的参数的类型必须具有相同的类型

This is then the only way I could make it work with type arguments (changing btw name of type argument to X, not to be confused with name of class A, and removing self type declaration, which I think is irrelevant for this problem/discussion): 然后这是我可以使用类型实参的唯一方法(将类型实参的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))
    ...
}

If someone could give a more 'type-restrictive' solution, I would really like to see it. 如果有人可以提供更具“类型限制”的解决方案,我真的很想看到它。


Another option, as Rui has suggested, would be to use a type member, something like this: 正如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))
    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM