An elegant way to implement an enumeration using a case object

I am trying to simulate the behavior of enum using a case object. It feels a little detailed, not elegant, and I was wondering if there is a better way to achieve this.

So here is an example:

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" } 

Is there a better way to get this inverse conversion from a string to the actual object of an object? Or is it generally better to implement this?

+8
enums scala
source share
3 answers

I prefer this approach:

 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) } 

to get the value:

 ProgressStatus.IN_PROGRESS.value 

to get all values:

 ProgressStatus.values 
+10
source share

The main enumerations in Scala awkward:

  • If you want to use them against a pattern, you will not see the following warning by the compiler “match cannot be exhaustive”, and you may encounter scala.MatchError unexpectedly at run time.
  • They are not compatible with the Javas enum - it’s not very scary if you don’t support the Java API, but if you do, it can be an unexpected disappointment for you.
  • Overloading with Scala transfers that do not work due to the fact of the same type of transfers after deletion. So, the following snapshot of the code is invalid:

     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 a weekend" def f(x: WeekDays.Value) = "That a weekday" } 

It will throw error: double definition: have the same type after erasure: (x: Enumeration#Value)String . As you can see, scala.Enumeration not user friendly and prefers not to use it, it will make your life easier.

Right approaches: The right approach uses a combination of case object or object with the sealed class:

 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) } 

In addition, you cannot use a wrapper object to enumerate:

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

Using a third-party library - Enumeratum can also solve the scala.Enumeration problem, it is a type-safe and powerful enumeration implementation and is easy to use and understand.

  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 } 
+2
source share

I prefer this approach

 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

what's nice about it, you can use this template

 object EnumImplicits { /** * Produce a JSON formatter for the Enum type * * eg 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()) } } 
-2
source share

All Articles