使用案例对象实现枚举的优雅方法

问题描述:

我想模拟使用案例对象的枚举行为。 它感觉有点冗长,而且不够优雅,我想知道是否有更好的方法来实现这一点。使用案例对象实现枚举的优雅方法

所以这里有一个例子:

sealed trait Operator 
object Operator{ 
    def apply(value: String) = value match { 
    case EqualsOp.name => EqualsOp 
    case NotEqualOp.name => NotEqualOp 
    case ContainsOp.name => ContainsOp 
    case NotContainsOp.name => NotContainsOp 
    case _ => UnknownOp 
    } 
} 

case object EqualsOp extends Operator { val name = "equals" } 
case object NotEqualOp extends Operator { val name = "not_equals" } 
case object ContainsOp extends Operator { val name = "contains" } 
case object NotContainsOp extends Operator { val name = "not_contains" } 

有没有更好的方式来获得一个字符串的实际情况对象这个反向映射? 或者一般来说更好地实现这个?

我更喜欢这样的做法:

sealed case class ProgressStatus(value: String) 

object ProgressStatus { 
    object IN_PROGRESS extends ProgressStatus("IN_PROGRESS") 
    object ACCEPTED extends ProgressStatus("ACCEPTED") 
    object REJECTED extends ProgressStatus("REJECTED") 

    val values = Seq(IN_PROGRESS, ACCEPTED, REJECTED) 
} 

得到一个值:

ProgressStatus.IN_PROGRESS.value 

获得的所有值:

+1

谢谢!但是,这基本上与我写的相同。在你的例子中,仍然没有办法将字符串映射到相应的case对象。 – Tomer

+0

它是,例如:'''ProgressStatus(“ACCEPTED”)'''匹配ProgressStatus.ACCEPTED''' –

+0

好的方法! – Dani

我更喜欢这样的做法

object ServiceState extends Enum { 
    sealed trait EnumVal extends Value with Serializable 
    val ERROR = new EnumVal { val name = "error" } 
    val OK = new EnumVal { val name = "ok" } 
} 

ScalaEnum

什么是好的关于这是你可以在Scala使用这个模式

object EnumImplicits { 
    /** 
    * Produce a JSON formatter for the Enum type 
    * 
    * e.g. implicit val interactionLineReasonFormat = enumFormat(InteractionLineReason) 
* 
* @param ev The enclosing enum "object" to provide a formatter for that extends Enum 
* @tparam A Implied from "ev" 
* @return A JSON reader and writer format 
*/ 
def enumFormat[A <: Enum](ev: A): Format[A#EnumVal] = 
new Format[A#EnumVal] { 
    override def reads(json: JsValue): JsResult[A#EnumVal] = { 
    json match { 
     case JsString(s) => 
     ev.values.find(_.name == s).map(JsSuccess(_)).getOrElse(JsError(s"$s is not a valid InteractionType")) 
     case _ => 
     JsError(s"${json.toString()} is not a valid InteractionType") 
    } 
    } 
    override def writes(o: A#EnumVal): JsValue = JsString(o.toString()) 
} 
} 

基本enumerations很笨拙:

  1. 如果你想在模式匹配使用它们,你会编译器看不到下一个警告“匹配可能不完全”而且你可以意外地面对scala.MatchError在肆意我。
  2. 它们与Java的枚举不兼容 - 如果您不支持Java的API,那么它不是很可怕,但如果您这样做了,它可能会给您带来意想不到的失望。
  3. 由于擦除后相同类型的枚举,Scala的枚举无法工作的重载。所以接下来的代码快照是无效的:

    object WeekDays extends Enumeration { 
        val Mon, Tue, Wed, Thu, Fri = Value 
    } 
    
    object WeekEnds extends Enumeration { 
        val Sat, Sun = Value 
    } 
    
    object DaysOperations { 
        def f(x: WeekEnds.Value) = "That's a weekend" 
        def f(x: WeekDays.Value) = "That's a weekday" 
    } 
    

它会抛出error: double definition: have the same type after erasure: (x: Enumeration#Value)String。 正如你看到的,scala.Enumeration不方便用户,更喜欢不使用它,它会让你的生活更轻松。

右途径: 正确的方法是使用case objectobject的结合sealed类:

object WeekDays { 
    sealed trait EnumVal 
    case object Mon extends EnumVal 
    case object Tue extends EnumVal 
    case object Wed extends EnumVal 
    case object Thu extends EnumVal 
    case object Fri extends EnumVal 
    val daysOfWeek = Seq(Mon, Tue, Wed, Thu, Fri) 
} 

此外,你可以不使用wrapper object的枚举:

sealed trait Day { def description: String } 
case object Monday extends Day { val description = "monday is awful" } 

利用第三方库 - Enumeratum也可以解决问题scala.enumeration,它是一个类型安全和强大的枚举实现,易于使用和理解。

libraryDependencies ++= Seq(
    "com.beachape" %% "enumeratum" % enumeratumVersion 
    ) 

    import enumeratum._ 

    sealed trait Day extends EnumEntry 

    object Greeting extends Enum[Greeting] { 
     val values = findValues 

     case object Mon  extends Day 
     case object Tue  extends Day 
     case object Wed  extends Day 
     case object Thu  extends Day 
     case object Fri  extends Day 
    }