scala: add methods to an enum

匿名 (未验证) 提交于 2019-12-03 08:54:24

问题:

I have a simple enum like this:

object ConditionOperator extends Enumeration {    val Equal           = Value("equal")   val NotEqual        = Value("notEqual")   val GreaterOrEqual  = Value("greaterOrEqual")   val Greater         = Value("greater")   val LessOrEqual     = Value("lessOrEqual")   val Less            = Value("less")

And I'd like to add a method to each enum so that I can use it like this:

def buildSqlCondition(field: String, operator: ConditionOperator.Value, value: String ) = {   val sqlOperator = operator.toSql   [...]

So, ConditionOperator.Equal.toSql wuld return "=", and ConditionOperator.NotEqual.toSql would return "<>", etc...

But I don't know how to define a toSql method, so that each enum can "see" it's own value and decide how to translate itself to a sql operator...

回答1:

This is an example of what I have found for Scala 2.9.2 from various searches on the topic in the past:

object Progress extends Enumeration {     type enum = Value      val READY = new ProgressVal {       val isActive = false       def myMethod: Any = { .. }     }      val EXECUTE = new ProgressVal {       val isActive = true       def myMethod: Any = { .. }     }      val COMPLETE = new ProgressVal {       val isActive = false       def myMethod: Any = { .. }     }      protected abstract class ProgressVal extends Val() {       val isActive: Boolean       def myMethod: Any     }     implicit def valueToProgress(valu: Value) = valu.asInstanceOf[ProgressVal] } type Progress = Progress.enum
  • The implicit is key to making this usable.

  • The type enum and type Progress are somewhat redundant; I include them to present both concepts as something I've found helpful.


To give credit where it's due, the original idea for this came from Sean Ross in a response to a question of which this one is a duplicate.



回答2:

You can start by defining an inner class that overrides Enumeration.Val. To simplify things, let's call it Value (we overload the original meaning of Value as defined in Enumeration). So we have our new Value type which inherits Enumeration.Val which itself inherits Enumeration.Value.

Given that toSql has no side effects and returns a different string for each enumeration, you might as well just make it a val.

Finally, to make it fully usable, you'll want to have ConditionOperator.apply and ConditionOperator.withName to return your newly defined Value class instead of the Value type as defined in Enumeration. Otherwise, when using apply or withName to look up an instance of ConditionOperator by index/name, you won't be able to call toSql because the enumeration type will not be specific enoough. Ideally we'd like to just override apply and withName and add a cast to ConditionOperator.Value, but these methods are final. However we can employ a small trick here: define new methods apply and withName with the same signature but an additional implicit parameter that will always be available (Predef.DummyImplicit fits this rolle perfectly). The additional parameter ensures that the signature is different so that we are able to define these new methods, while at the same time being nearly indistinguishable from the original apply/withName methods. The rules for overloading resolution in scala ensure that our new methods are the ones favored by the compiler (so we have in practice shadowed the original methods).

object ConditionOperator extends Enumeration {   // Here we overload the meaning of "Value" to suit our needs   class Value(name: String, val toSql: String) extends super.Val(name) {     def someFlag: Boolean = true // An example of another method, that you can override below   }   val Equal           = new Value("equal", "=")   val NotEqual        = new Value("notEqual", "<>")   val GreaterOrEqual  = new Value("greaterOrEqual", ">=")   val Greater         = new Value("greater", ">")   val LessOrEqual     = new Value("lessOrEqual", "<=") { override def someFlag = false }   val Less            = new Value("less", "<")     final def apply(x: Int)( implicit dummy: DummyImplicit ): Value = super.apply(x).asInstanceOf[Value]   final def withName(s: String)( implicit dummy: DummyImplicit ): Value = super.withName(s).asInstanceOf[Value] }

You can check that you can now do things like ConditionOperator(2).toSql or ConditionOperator.withName("greaterOrEqual"), which both return ">=" as expected. Finally, the above gymnastic can be abstracted away:

abstract class CustomEnumeration extends Enumeration {   type BaseValue = super.Val   type CustomValue <: super.Value   type Value = CustomValue   final def apply(x: Int)( implicit dummy: DummyImplicit ): CustomValue = super.apply(x).asInstanceOf[CustomValue]   final def withName(s: String)( implicit dummy: DummyImplicit ): CustomValue = super.withName(s).asInstanceOf[CustomValue] } object ConditionOperator extends CustomEnumeration {   class CustomValue(name: String, val toSql: String) extends BaseValue(name) {     def someFlag: Boolean = true   }   val Equal           = new Value("equal", "=")   val NotEqual        = new Value("notEqual", "<>")   val GreaterOrEqual  = new Value("greaterOrEqual", ">=")   val Greater         = new Value("greater", ">")   val LessOrEqual     = new Value("lessOrEqual", "<=") { override def someFlag = false }   val Less            = new Value("less", "<")   }


回答3:

object ConditionOperator extends Enumeration {    implicit def overrideValue(v:Value) = new OverridedValue(v)   class OverridedValue(val v:Value) {     def toSql = v.toString   }   val Equal           = Value("=")   val NotEqual        = Value("<>")   val GreaterOrEqual  = Value(">=")   val Greater         = Value(">")   val LessOrEqual     = Value("<=")   val Less            = Value("<")  }  import ConditionOperator._  assert(Equal.toSql == "=")

And in scala 2.10, you can make it simpler by using implicit class, replace

implicit def overrideValue(v:Value) = new OverridedValue(v) class OverridedValue(val v:Value) {   def toSql = v.toString }

with

implicit class OverridedValue(val v:Value) {   def toSql = v.toString }


转载请标明出处:scala: add methods to an enum
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!