【问题标题】:Scala implicit conversion on generic trait implementing Java interfaceScala隐式转换实现Java接口的通用特征
【发布时间】:2012-05-11 21:54:41
【问题描述】:

我几天来一直在研究隐式转换的问题,但不知何故,我无法弄清楚我做错了什么。我通读了关于 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) 有没有更好的方法来做我想要实现的目标?同样,这不是我想要做的实际事情,但已经足够接近了。

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

【问题讨论】:

    标签: scala generics implicit-conversion


    【解决方案1】:

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

    例如,您可以添加

    implicit def view: DTOType => EntityType
    

    作为 trait 中的一个方法,然后它将被编译。 (你也可以删除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 为你应用隐式转换,这样你就认为你有一个从 DTOType 映射到 @ 的 persist 987654333@,但您实际上只有 Java 接口所需的 EntityTypeEntityType 转换。


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

    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,谁接D,你需要转换哪个方向?)

    【讨论】:

    • 感谢您的快速回答雷克斯。仍然让我感到困惑的是,对于 Predef 隐式(如 byte2short),如果您想使用转换,则不必指定该抽象 def。我预计转换将是可用的......嗯,隐含的。特别是在我的问题 1 中,我希望 scala 一旦知道类型就可以解决转换。至少我希望,如果我在您的示例中将Mappings._ 带入Persister 的范围内,我就不必再次实现隐式def。对我来说,我到底做错了什么仍然是个谜……
    • @LeChe - 我已经更新了我的答案,使用“让外部隐式转换执行”建议显示了一个等效的工作。
    • 当然,我真是个白痴!为什么 trait 需要知道 A-tree,转换对它来说应该是完全透明的!妈的,我现在觉得自己好傻。非常感谢您的解释。
    猜你喜欢
    • 2011-11-25
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 2021-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多