可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
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 }