java - 将类型类添加到 Java 枚举 - 无需模拟

标签 java scala enums typeclass simulacrum

我正在尝试将几个 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 !!
  }
}

非常感谢有关错误消息的任何说明。

最佳答案

这是因为您的隐式类及其定义的对象都称为 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 和隐式 def EmailEnumOps。因此,当您导入 EmailEnumOps._ 时,隐式 def 会被对象遮盖,因此 TradeEnum.CLEARED 无法隐式转换。

此行为如 language specification 中指定。 :

Bindings of different kinds have a precedence defined on them:

  1. Definitions and declarations that are local, inherited, or made available by a package clause and also defined in the same compilation unit as the reference, have highest precedence.

  2. Explicit imports have next highest precedence.

  3. Wildcard imports have next highest precedence.

  4. Definitions made available by a package clause, but not also defined in the same compilation unit as the reference, have lowest precedence.

关于java - 将类型类添加到 Java 枚举 - 无需模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41917445/

相关文章:

Scala 使用最终静态变量

ios - 从枚举类型 'enum CGImageAlphaInfo' 隐式转换为不同的枚举类型 'CGBitmapinfo' (aka) 'enum CGBitmapInfo' ) ios 7

java - 抽象类或接口(interface)

java - 使用 AWSCredentials 构建 AmazonS3Client 实例的首选方法

java - 玩! Intellij IDEA 中的 2.0 控制台将无法工作

Scala:将元素添加到索引上的 ArrayBuffer

C# Enum 忽略名称的顺序

c - 位域中的枚举 - ANSI C

java - 解析一行数据: split vs regex

java - 将枚举视为类中的常规属性