【问题标题】:Adding a Typeclass to Java enum - without simulacrum将 Typeclass 添加到 Java 枚举 - 没有 simulacrum
【发布时间】:2017-01-29 03:53:11
【问题描述】:

我正在尝试将几个 Java 枚举值转换为另一个 Java 枚举(Java 枚举优于 Scala 枚举的使用是由于遗留原因 - 枚举实际上是使用 JAXB 生成的)。

我认为编写一个类型类看起来更简洁(也有点酷),而不是一个普通的旧模式匹配和一个映射将一个枚举类型映射到另一个。当我使用 simulacrum 执行此操作时,它编译并运行得非常好。但是,当我尝试自己手动编写类型类时,会引发编译错误

[error] /Users/arun/IdeaProjects/AdvancedScala/src/main/scala/MultipleToSingleEnum.scala:32: value toEmail is not a member of TradeEnum
[error]     println (TradeEnum.CLEARED.toEmail)

Java 枚举的代码是:

源枚举

public enum TradeEnum {
    CONFIRMED, CLEARED
}

public enum SeriesEnum {
    CREATED,DELETED
}

目标枚举

public enum EmailEnum {
    T_CONFIRMED, T_CLEARED, S_CREATED, S_DELETED
}

使用 Simulacrum 的类型类(工作得很好!)

import simulacrum._


@typeclass trait EmailEnumConvertibleSim[A]{
  def toEmailEnum(value:A):Option[EmailEnum]
}

object EmailEnumConvertibleSim{
  implicit val tradeToEmailEnum = new EmailEnumConvertibleSim[TradeEnum]{
    private val map=Map(
      TradeEnum.CLEARED -> EmailEnum.T_CLEARED,
      TradeEnum.CONFIRMED -> EmailEnum.T_CONFIRMED
    )
    override def toEmailEnum(value: TradeEnum): Option[EmailEnum] = map.get(value)
  }

  implicit val seriesToEmailEnum = new EmailEnumConvertibleSim[SeriesEnum]{
    private val map=Map(
      SeriesEnum.CREATED -> EmailEnum.S_CREATED,
      SeriesEnum.DELETED -> EmailEnum.S_DELETED
    )
    override def toEmailEnum(value: SeriesEnum): Option[EmailEnum] = map.get(value)
  }
}

import EmailEnumConvertibleSim.ops._

object MultipleToSingleEnumSim {
  def main(args: Array[String]): Unit = {
    println (TradeEnum.CLEARED.toEmailEnum)
  }
}

手动编码类型类 (Ops)

trait EmailEnumConvertible[A]{
  def toEmailEnum(value:A):Option[EmailEnum]
}

object EmailEnumConvertible{
  implicit val tradeToEmailEnum = new EmailEnumConvertible[TradeEnum]{
    private val map=Map(
      TradeEnum.CLEARED -> EmailEnum.T_CLEARED,
      TradeEnum.CONFIRMED -> EmailEnum.T_CONFIRMED
    )
    override def toEmailEnum(value: TradeEnum): Option[EmailEnum] = map.get(value)
  }

}

object EmailEnumOps{
  implicit class EmailEnumOps[A] (value:A){
    def toEmail()(implicit emailConvertable:EmailEnumConvertible[A]):Option[EmailEnum]={
      emailConvertable.toEmailEnum(value)
    }
  }
}

import EmailEnumOps._

object MultipleToSingleEnum {
  def main(args: Array[String]): Unit = {
    println (TradeEnum.CLEARED.toEmail) //ERROR IS REPORTED HERE !!
  }
}

高度赞赏错误消息的任何提示。

【问题讨论】:

  • 看起来您正在将模拟代码中的内容混合到您的手写类型类代码中。
  • 非常感谢@Jasper-M。那是重构的结果。错误仍然存​​在。
  • 这可能和stackoverflow.com/questions/30863699/…是同一个问题吗?
  • 不幸的是,看起来不像@Jasper-M。我没有得到“隐式未找到”。只是没有找到该功能。我可以看到我在做一些愚蠢的事情。非常感谢您对此进行调查。

标签: java scala enums typeclass simulacrum


【解决方案1】:

这是因为你的隐式类和它定义的对象都称为EmailEnumOps

当你改变它的工作对象的名称时:

trait EmailEnumConvertible[A]{
  def toEmailEnum(value: A): Option[EmailEnum]
}

object EmailEnumConvertible{
  implicit val tradeToEmailEnum: EmailEnumConvertible[TradeEnum] = new EmailEnumConvertible[TradeEnum]{
    private val map = Map(
      TradeEnum.CLEARED -> EmailEnum.T_CLEARED,
      TradeEnum.CONFIRMED -> EmailEnum.T_CONFIRMED
    )
    override def toEmailEnum(value: TradeEnum): Option[EmailEnum] = map.get(value)
  }

}

object AnyOtherName{
  implicit class EmailEnumOps[A] (value: A){
    def toEmail()(implicit emailConvertable:EmailEnumConvertible[A]): Option[EmailEnum]={
      emailConvertable.toEmailEnum(value)
    }
  }
}

import AnyOtherName._

object MultipleToSingleEnum {
  def main(args: Array[String]): Unit = {
    println (TradeEnum.CLEARED.toEmail) // No error :-)
  }
}

看起来,当在当前作用域中定义了一个对象,然后你导入了一个同名的成员时,第一个对象仍然会隐藏导入的成员。

scala> :paste
// Entering paste mode (ctrl-D to finish)

object A { def B(a: Int) = "foo" }
object B 
import A._
B(4)

// Exiting paste mode, now interpreting.

<pastie>:41: error: B.type does not take parameters
       B(4)
        ^

一个隐式类EmailEnumOps被编译成一个类EmailEnumOps和一个隐式defEmailEnumOps。因此,当您导入 EmailEnumOps._ 时,隐式 def 被对象遮蔽,因此无法隐式转换 TradeEnum.CLEARED

此行为在language specification 中指定:

不同种类的绑定有一个定义的优先级:

  1. 本地、继承或创建的定义和声明 由包子句提供并且也在同一编译中定义 单位作为参考,具有最高优先级。

  2. 显式导入具有 下一个最高优先级。

  3. 通配符导入次之 优先级。

  4. 包子句提供的定义,但不是 也定义在与参考相同的编译单元中,有 最低优先级。

【讨论】:

  • 哇!!!!让我看起来比我最初预期的更愚蠢。万分感谢 !!效果很好。
  • "一个隐式类 EmailEnumOps 被编译成一个类 EmailEnumOps 和一个隐式 def EmailEnumOps。所以当你导入 EmailEnumOps._ 时,隐式 def 被对象遮蔽,因此 TradeEnum.CLERED 不能被隐式转换了。” - 甜蜜!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多