在 Scala 中使用泛型实现 trait 的正确方法是什么?

Rob*_*Rob 6 generics scala type-bounds

我有一些简单的特征(下面示例中的实体),它们由我的应用程序中的案例类扩展。我想创建一个 EntityMapper 特征,它提供一个接口来处理扩展实体特征的案例类(下面示例中的 Foo )。我认为我应该能够使用泛型和边界很容易地做到这一点,但我已经花了几个小时在它上面,我还没有让它正常工作。下面的代码是我认为我应该能够做的,但它因编译器错误而失败。错误是

Test.scala:15: 错误:值 id 不是类型参数 Foo \ println(e.id) 的成员

package experiment

trait Entity {
    val id: Option[Long]
}

case class Foo(val id: Option[Long] = None) extends Entity

trait EntityMapper {
    def create[E <: Entity](e: E): E
}

object FooMapper extends EntityMapper {
    def create[Foo](e: Foo): Foo = {
        println(e.id)
        e
    }
}

object Main extends App {
    val foo = FooMapper.create(Foo(None))
}
Run Code Online (Sandbox Code Playgroud)

我尝试了几种不同的方法来解决问题,但没有任何效果。如果我注释掉有问题的行“println(e.id)”,它会编译,但这没有用,因为我无法访问或修改 Foo 的任何属性。

我曾尝试对映射器特征使用协变参数,然后将类型提供给 FooMapper 对象定义,但这会产生相同的错误。该尝试的代码如下:

trait EntityMapper[+Entity] {
    def create[E <: Entity](e: E): E
}

object FooMapper extends EntityMapper[Foo] {
...
}
Run Code Online (Sandbox Code Playgroud)

我也尝试过用简单的继承来实现同样的事情,但我不能正确地限制 FooMapper 中的类型参数只接受 Foos,我必须使方法签名完全匹配特征,这就是为什么我开始尝试使用泛型来实现它类型绑定。该尝试的代码如下:

trait EntityMapper {
    def create(e: Entity): Entity
}

object FooMapper extends EntityMapper {
    def create(e: Foo): Foo = {
        println(e.id)
        e
    }
}
Run Code Online (Sandbox Code Playgroud)

返回的错误代码是:

Test.scala:13: 错误:对象创建不可能,因为方法在类型为 (e:experiment.Entity)experiment.Entity 的 trait EntityMapper 中创建

(注意experiment.Entity与experiment.Foo不匹配:experiment包中的Foo类是experiment包中trait Entity的子类,但方法参数类型必须完全匹配。)

object FooMapper extends EntityMapper {
       ^
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激。我正在使用 Scala 版本 2.10.3。

小智 8

您可以通过多种方式修复错误

1.在特征上指定泛型类型约束。

trait EntityMapper[E <: Entity] {
  def create(e: E): E
}

object FooMapper extends EntityMapper[Foo] {
  def create(e: Foo): Foo = {
    println(e.id)
    e
  }
}
Run Code Online (Sandbox Code Playgroud)

2.使用参数化类型

trait EntityMapper {
  type E <: Entity
  def create(e: E): E
}

object FooMapper extends EntityMapper {
  type E = Foo
  def create(e: Foo): Foo = {
    println(e.id)
    e
  }
}
Run Code Online (Sandbox Code Playgroud)

查看Scala: Abstract types vs generics以了解有关这两种方法的更多背景知识。