Scala隐式转换实现Java接口的通用特性

时间:2012-05-11 21:54:41

标签: scala generics implicit-conversion

我几天来一直在研究隐式转换的问题,但不知怎的,我无法弄清楚我做错了什么。我阅读了有关涉及隐含的SO的所有其他问题,但我仍然不明白问题是什么。

作为一个例子,让我们考虑像这样的Java接口(为了简洁,T扩展了Object):

public interface JPersistable<T extends Object> {
    public T persist(T entity);
}

在scala中,我执行以下操作:

case class A()
case class B() extends A
case class C()
case class D() extends C

trait Persistable[DTOType <: A, EntityType <: C] {
  // this would be implemented somewhere else
  private def doPersist(source: EntityType): EntityType = source

  // this does not implement the method from the Java interface
  private def realPersist(source: DTOType)(implicit view: DTOType => EntityType): EntityType = doPersist(source)

  // this DOES implement the method from the Java interface, however it throws:
  // error: No implicit view available from DTOType => EntityType.
  def persist(source: DTOType): EntityType = realPersist(source)
}

case class Persister() extends Persistable[B, D] with JPersistable[B]

object Mappings {
  implicit def BToD(source: B): D = D()
}

object Test {
  def main(args: Array[String]) {

    import Mappings._
    val persisted = Persister().persist(B())
  }
}

如评论中所述,我在编译时遇到异常。我想我的问题是:

1)为什么我需要明确指定doRealPersist的隐式转换?即使我执行以下操作,我预计转换也会发生:

trait Persistable[DTOType <: A, EntityType <: C] {
  // this would be implemented somewhere else
  private def doPersist(source: EntityType): EntityType = source

  def persist(source: DTOType): EntityType = doPersist(source)
}

但是,这也不能编译。

2)为什么编译在persist失败而在实际方法调用(val persisted = Persister().persist(B()))失败?这应该是第一个知道实际类型的EntityType和DTOType的地方,对吗?

3)有没有更好的方法来实现我想要实现的目标?再一次,这不是我想要做的实际事情,但足够接近。

如果这个问题无知,请提前道歉,并提前感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

您需要在特征中进行转换。您无法隐式地从外部传递它,因为外部不知道persist秘密需要realPersist,这需要隐式转换。即使没有考虑JPersistable,这一切都失败了。

您可以添加

implicit def view: DTOType => EntityType

作为特征中的方法,然后编译。 (你也可以放弃realPersist。)

然后你需要一种方法来获得该视图集。你可以

case class Persister()(implicit val view: B => D) extends Persistable[B,D]

然后你们都很好。 (implicit val满足特征的implicit def。)

但是现在你遇到了更大的问题:你的Java界面签名与你的Scala签名不匹配。等效的Scala是

trait JPersistable[T <: Object] { def persist(t: T): T }

了解persist如何获取并返回相同的类型?并了解它在Scala类中不是的方式?这不会起作用,也不应该起作用!所以你必须重新考虑你在这里想要完成的事情。也许你只想让隐式转换可用 - 不要将它传递给方法! - 并让Scala为你应用隐式转换,这样你就可以认为你有persist来自{{1}转到DTOType,但您实际上只有Java接口所需的EntityTypeEntityType转换。


编辑:例如,这是您使用标准隐式转换发布的工作版本:

EntityType

注意哪些类型在哪里使用! (谁需要trait JPer[T] { def persist(t: T): T } class A case class B() extends A class C case class D() extends C trait Per[Y <: C] extends JPer[Y] { private def doIt(y: Y): Y = y def persist(y: Y) = doIt(y) } case class Perer() extends Per[D] // "with JPer" wouldn't add anything! object Maps { implicit def BtoD(b: B): D = D() } object Test extends App { import Maps._ val persisted = Perer().persist(B()) } ,谁需要B以及您需要转换哪个方向?)