Неявное преобразование Scala на универсальной особенности, реализующей интерфейс Java

Я работаю над проблемой неявного преобразования уже несколько дней, но почему-то просто не могу понять, что я делаю неправильно. Я прочитал все другие вопросы по 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 ответ

Решение

Вы должны сделать конверсию доступной в черте. Вы не можете передать это извне неявно, потому что внешнее не знает, что persist тайно требует realPersist который требует неявного преобразования. Это все терпит неудачу даже без учета JPersistable,

Вы можете, например, добавить

implicit def view: DTOType => EntityType

как метод в признаке, и он будет компилироваться. (Вы можете бросить realPersist тогда тоже.)

Тогда вам нужен способ установить это представление. Вы можете

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

и тогда у тебя все хорошо. (The implicit val удовлетворяет implicit def черты.)

Но теперь у вас есть большие проблемы: ваша подпись Java-интерфейса не совпадает с вашей подписью Scala. Эквивалент Скала

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

Смотри как persist берет и возвращает тот же тип? И видите, как это не в вашем классе Scala? Это не сработает, и не должно! Таким образом, вы должны переосмыслить именно то, что вы пытаетесь достичь здесь. Может быть, вы просто хотите сделать неявное преобразование доступным - не передавайте его методу!- и пусть Scala применит для вас неявное преобразование, так что вы думаете, что получили persist что карты из DTOType в EntityType, но у вас действительно просто есть EntityType в EntityType преобразовать, что требует интерфейс Java.


Редактировать: например, вот рабочая версия того, что вы опубликовали, просто используя стандартное неявное преобразование:

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 а в каком направлении вам нужна конверсия?)

Другие вопросы по тегам