2016-03-03 80 views
8

我想模擬使用案例對象的枚舉行爲。 它感覺有點冗長,而且不夠優雅,我想知道是否有更好的方法來實現這一點。使用案例對象實現枚舉的優雅方法

所以這裏有一個例子:

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

有沒有更好的方式來獲得一個字符串的實際情況對象這個反向映射? 或者一般來說更好地實現這個?

回答

10

我更喜歡這樣的做法:

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

-2

我更喜歡這樣的做法

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

基本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 
    }