在Scala中使用泛型实现特征的正确方法是什么? [英] What is the correct way to implement trait with generics in Scala?

查看:136
本文介绍了在Scala中使用泛型实现特征的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些简单的特征(在下面的示例中为Entity),这些特征通过我的应用中的案例类进行了扩展.我想创建一个EntityMapper特性,它提供一个接口来处理扩展了Entity特性的案例类(在下面的示例中为Foo).我以为我应该可以使用泛型和边界相当容易地做到这一点,但是我已经花了几个小时在上面,但我还没有使其正常工作.以下代码是我认为应该能够执行的操作,但是由于编译器错误而失败.错误是

I have some simple traits (Entity in the example below) that are extended by case classes in my app. I would like to create an EntityMapper trait that provides an interface for handling the case classes that extend the Entity trait (Foo in the example below). I thought I should be able to do this fairly easily using generics and bounding but I've spent a couple of hours on it already and I haven't gotten it to work correctly. The code below is what I think I should be able to do but it fails with a compiler error. The error is

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

Test.scala:15: error: value id is not a member of type parameter 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))
}

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

I've tried several different things to solve the problem but nothing has worked. If I comment out the line in question "println(e.id)", it compiles but that is not useful because I cannot access or modify any of the properties of Foo.

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

I have tried using a covariant argument to the mapper trait and then supplying the type to the FooMapper object definition but that yields the same error. The code for that attempt is below:

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

object FooMapper extends EntityMapper[Foo] {
...
}

我也尝试通过简单的继承来实现相同的目的,但是我不能正确地限制FooMapper中的类型参数仅采用Foos,我必须使方法签名与特征完全匹配,这就是为什么我开始尝试使用来实现它的原因具有类型绑定的泛型.该尝试的代码如下:

I have also tried achieving the same thing with simple inheritance but I cannot correctly restrict the type parameter in FooMapper to only take Foos, I have to make the method signature match the trait exactly which is why I started trying to implement it using generics with a type bound. The code for that attempt is below:

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

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

返回的错误代码是:

Test.scala:13:错误:无法创建对象,因为未定义类型为(e:experiment.Entity)experiment.Entity的特征EntityMapper中的方法

Test.scala:13: error: object creation impossible, since method create in trait EntityMapper of type (e: experiment.Entity)experiment.Entity is not defined

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

(Note that experiment.Entity does not match experiment.Foo: class Foo in package experiment is a subclass of trait Entity in package experiment, but method parameter types must match exactly.)

object FooMapper extends EntityMapper {
       ^

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

Any help would be greatly appreciated. I'm using Scala version 2.10.3.

推荐答案

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

You can fix the error in a couple of ways

1.在特征上指定通用类型约束.

1.Specifying the generic type constraint on the trait.

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

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

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

查看 Scala:抽象类型与泛型,以获取有关两种方法.

Look at Scala: Abstract types vs generics to get some more background on the two approaches.

这篇关于在Scala中使用泛型实现特征的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆