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