使用案例对象实现枚举的优雅方法
问题描述:
我想模拟使用案例对象的枚举行为。 它感觉有点冗长,而且不够优雅,我想知道是否有更好的方法来实现这一点。使用案例对象实现枚举的优雅方法
所以这里有一个例子:
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
获得的所有值:
答
我更喜欢这样的做法
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" }
}
什么是好的关于这是你可以在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
很笨拙:
- 如果你想在模式匹配使用它们,你会编译器看不到下一个警告“匹配可能不完全”而且你可以意外地面对
scala.MatchError
在肆意我。 - 它们与Java的枚举不兼容 - 如果您不支持Java的API,那么它不是很可怕,但如果您这样做了,它可能会给您带来意想不到的失望。
-
由于擦除后相同类型的枚举,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 object
或object
的结合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
}
谢谢!但是,这基本上与我写的相同。在你的例子中,仍然没有办法将字符串映射到相应的case对象。 – Tomer
它是,例如:'''ProgressStatus(“ACCEPTED”)'''匹配ProgressStatus.ACCEPTED''' –
好的方法! – Dani