F-bounded多态中子类型的Scala重写类型参数

时间:2015-06-28 08:52:43

标签: scala generics types polymorphism

我正在尝试创建一个特征Entity,强制其子类型有两种状态:TransientPersistent

trait EntityState
trait Transient extends EntityState
trait Persistent extends EntityState
trait Entity[State <: EntityState]

例如,一个子类class Post[State <: EntityState] extends Entity[State]可以实例化为new Post[Persistent]new Post[Transient]

接下来,我正在为特征Entity添加一些方法,这些方法可以根据State来调用:

trait Entity[State <: EntityState] {
    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Entity[Persistent]
}

要解释,对于任何扩展Entity的类,只有当类处于状态id时才能调用方法Persistent(即它已保存到数据库中并且已经存在分配了一个自动生成的id。。

另一方面,仅当类为persist(尚未保存到数据库)时才能调用方法Transient。方法persist用于将调用者类的实例保存到数据库,返回类的Persistent版本。

现在,问题是我希望persist的返回类型是调用者类的返回类型。例如,如果我在类persist的实例上调用Post[Transient],则应该返回Post[Persistent]而不是Entity[Persistent]

我四处搜寻,找到了一个叫F-Bounded Polymorphism的东西。我正在尝试很多方法来适应它来解决我的问题,但仍然无效。这是我做的:

首先尝试:

trait Entity[State <: EntityState, Self[_] <: Entity[State,Self]] {
    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Self[Persistent]
}

class Post[State <: EntityState] extends Entity[State, ({type λ[B] == Post[State]})#λ] {

    def persist(implicit ev: <:<[State, Transient]): Post[State] = {
        ???
    }
}

在上面的类Post中,我使用Eclipse的自动完成来生成方法persist的实现,并发现它的返回类型仍然不正确。

第二次尝试:

class Post[State <: EntityState] extends Entity[State, Post] {

   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

有了这个,看起来是正确的,除了它有一个编译错误:

[error] D:\playspace\myblog\app\models\post\Post.scala:14: kinds of the type arguments (State,models.post.Post) do not conform to the expected kinds of the type parameters (type State,type Self) in trait Entity.
[error] models.post.Post's type parameters do not match type Self's expected parameters:
[error] type State's bounds <: common.models.EntityState are stricter than type _'s declared bounds >: Nothing <: Any
[error] trait Post[State <: EntityState] extends Entity[State, Post] {

2 个答案:

答案 0 :(得分:3)

我相信这就是你想要做的事情:

trait Entity[State <: EntityState, Self[S<:EntityState] <: Entity[S, Self]] {
  _: Self[State] =>

    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Self[Persistent]
}


abstract class Post[State <: EntityState] extends Entity[State, Post] {
   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

更新 _: Self[State] =>部分是自我类型注释。它表示任何混合Entity特征的类必须扩展Self[State](不这样做会导致编译时错误)。如果我们删除这个自我类型的注释,我们可能会定义类似这样的东西,编译器不会眨眼睛:

abstract class User[State <: EntityState] extends Entity[State, Post] {
   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

请注意User类如果将Entity类型参数设置为Self(而不是Post),User类扩展<console>:12: error: illegal inheritance; self-type User[State] does not conform to Entity[State,Post]'s selftype Post[State] abstract class User[State <: EntityState] extends Entity[State, Post] { ^ 。就编译器而言,这是有效的,但肯定不是你想到的。 使用自我类型注释,上面不会编译:

rm_between_multiple

答案 1 :(得分:2)

为什么普通的旧ad-hoc多态在这里不能满足你?例如,这是编译。

<div id="element" onclick="changeHim();">Change me upon click.</div>