[英]How to add a method to Enumeration in Scala?
在Java中你可以:
public enum Enum {
ONE {
public String method() {
return "1";
}
},
TWO {
public String method() {
return "2";
}
},
THREE {
public String method() {
return "3";
}
};
public abstract String method();
}
你是如何在Scala中做到這一點的?
編輯/有用的鏈接:
以下是通過擴展Enumeration.Val類向scala枚舉添加屬性的示例。
object Planet extends Enumeration {
protected case class Val(val mass: Double, val radius: Double) extends super.Val {
def surfaceGravity: Double = Planet.G * mass / (radius * radius)
def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity
}
implicit def valueToPlanetVal(x: Value) = x.asInstanceOf[Val]
val G: Double = 6.67300E-11
val Mercury = Val(3.303e+23, 2.4397e6)
val Venus = Val(4.869e+24, 6.0518e6)
val Earth = Val(5.976e+24, 6.37814e6)
val Mars = Val(6.421e+23, 3.3972e6)
val Jupiter = Val(1.9e+27, 7.1492e7)
val Saturn = Val(5.688e+26, 6.0268e7)
val Uranus = Val(8.686e+25, 2.5559e7)
val Neptune = Val(1.024e+26, 2.4746e7)
}
scala> Planet.values.filter(_.radius > 7.0e6)
res16: Planet.ValueSet = Planet.ValueSet(Jupiter, Saturn, Uranus, Neptune)
基於Chris的解決方案 ,您可以通過隱式轉換實現更好的語法:
object Suit extends Enumeration {
val Clubs, Diamonds, Hearts, Spades = Value
class SuitValue(suit: Value) {
def isRed = !isBlack
def isBlack = suit match {
case Clubs | Spades => true
case _ => false
}
}
implicit def value2SuitValue(suit: Value) = new SuitValue(suit)
}
然后你可以打電話,例如, Suit.Clubs.isRed
。
Scala 枚舉與Java枚舉不同。
目前,沒有辦法添加方法(以理智的方式)。 有一些解決方法,但沒有任何東西適用於所有情況,看起來不像語法垃圾。
我嘗試了類似的東西(在枚舉類的實例中添加方法,同時能夠在運行時創建新實例並在object
和類的new
實例之間具有工作等效關系),但是被錯誤#4023 ( “getClasses / getDeclaredClasses似乎錯過了一些(REPL)或所有(scalac)類(對象)聲明的”)。
我看看這些相關問題:
老實說,我不會使用Enumeration
。 這是一個源自Scala 1.0(2004)的類,它有很奇怪的東西,並沒有很多人(除了那些編寫它的人)在沒有教程的情況下理解如何使用它。
如果我絕對需要枚舉,我會用Java編寫該類。
如果你不需要迭代枚舉值或做一些其他的枚舉,我建議使用ADT而不是Enumeration
。
sealed abstract class Enum {
def method: String = this match {
case One => "1"
case Two => "2"
case Three => "3"
}
}
case object One extends Enum
case object Two extends Enum
case object Three extends Enum
這種方法比Enumeration
有一個優點,即編譯器會在您忘記match
表達式中的一個或多個案例時發出警告。
詳細闡述了Aaron的解決方案 ,Scala 2.10中使用隱式類的更緊湊的形式:
object Suit extends Enumeration {
val Clubs, Diamonds, Hearts, Spades = Value
implicit class SuitValue(suit: Value) {
def isRed = !isBlack
def isBlack = suit match {
case Clubs | Spades => true
case _ => false
}
}
}
然后你就可以這樣使用它: Suit.Clubs.isRed
你可以這樣做:
object Suit extends Enumeration {
val Clubs, Diamonds, Hearts, Spades = Value
def isRed(suit : Value) = !isBlack(suit)
def isBlack(suit : Value) = suit match {
case Clubs | Spades => true
case _ => false
}
}
顯然這不是完美的,但你可以這樣做:
Suit.isBlack(Suit.Clubs)
Scala的Enumeration不允許將屬性和/或方法添加到枚舉中的值。 有了這個新的MyEnumeration你就可以。
abstract class MyEnumeration {
// "Value" must be the name of the class defining your values type Value
type Value
// Contains your values in definition order
private val vals = collection.mutable.LinkedHashMap[String, Value]()
// A mixin for your values class to automatically collect the values
protected trait ValuesCollector { self: Value =>
private val ordinal = vals.size
vals += (fieldNames(ordinal) -> self)
def getName = fieldNames(ordinal)
override def toString = getName
}
def apply(ordinal: Int) = vals(fieldNames(ordinal))
def apply(fldName: String) = vals(fldName)
def values = vals.values
def namedValues: collection.Map[String, Value] = vals
// Getting the field names through reflection.
// Copied from scala.Enumeration
private val fieldNames = getClass.getMethods filter (m =>
m.getParameterTypes.isEmpty &&
classOf[ValuesCollector].isAssignableFrom(m.getReturnType) &&
m.getDeclaringClass != classOf[MyEnumeration]) map (_.getName)
}
在這里,您可以看到Scala中的Planet示例。
object Planet extends MyEnumeration {
case class Value(val mass: Double, val radius: Double) extends ValuesCollector {
// universal gravitational constant (m3 kg-1 s-2)
private val G = 6.67300E-11;
def surfaceGravity = G * mass / (radius * radius)
def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
}
val MERCURY = Value(3.303e+23, 2.4397e6)
val VENUS = Value(4.869e+24, 6.0518e6)
val EARTH = Value(5.976e+24, 6.37814e6)
val MARS = Value(6.421e+23, 3.3972e6)
val JUPITER = Value(1.9e+27, 7.1492e7)
val SATURN = Value(5.688e+26, 6.0268e7)
val URANUS = Value(8.686e+25, 2.5559e7)
val NEPTUNE = Value(1.024e+26, 2.4746e7)
val PLUTO = Value(1.27e+22, 1.137e6)
}
object PlanetTest {
def main(args: Array[String]) {
val earthWeight = 175
val mass = earthWeight/Planet.EARTH.surfaceGravity
for (p <- Planet.values) println("Your weight on %s is %f" format (p, p.surfaceWeight(mass)))
/* Your weight on MERCURY is 66.107583
* Your weight on VENUS is 158.374842
* Your weight on EARTH is 175.000000
* Your weight on MARS is 66.279007
* Your weight on JUPITER is 442.847567
* Your weight on SATURN is 186.552719
* Your weight on URANUS is 158.397260
* Your weight on NEPTUNE is 199.207413
* Your weight on PLUTO is 11.703031
*/
}
}
object Unit extends Enumeration {
abstract class UnitValue(var name: String) extends Val(name) {
def m: Unit
}
val G = new UnitValue("g") {
def m {
println("M from G")
}
}
val KG = new UnitValue("kg") {
def m {
println("M from KG")
}
}
}
如果你絕對需要為每個枚舉值設置方法並且需要能夠迭代值,你可以這樣做:
object BatchCategory extends Enumeration {
class BatchCategory extends Val {
val isOfficial, isTest, isUser = false
}
val OFFICIAL = new BatchCategory { override val isOfficial = true }
val TEST = new BatchCategory { override val isTest = true }
val USER = new BatchCategory { override val isUser = true }
// Needed to get BatchCategory from Enumeration.values
implicit def valueToBatchCategory(v: Value): BatchCategory = v match {
case bc: BatchCategory => bc
case x => throw new IllegalArgumentException("Value is not a BatchCategory: " + x)
}
def valueOf(catStr: String): BatchCategory = {
BatchCategory.values.
find { v => val s = v.toString; s.take(1) == catStr || s == catStr }.
getOrElse(throw new IllegalArgumentException("Unknown category '" + catStr + "' ! "))
}
def main(args: Array[String]) {
BatchCategory.values.foreach(v => println(v + " isOfficial=" + v.isOfficial))
}
}
版畫
OFFICIAL isOfficial=true
TEST isOfficial=false
USER isOfficial=false
這是針對一些遺留代碼完成的,除了Enumeration之外,這些代碼無法移動到更健全的枚舉策略。
看完scala.Enumeration的源代碼后,我得到了這個:
object MyEnum extends Enumeration {
val ONE = new Val { def method = "1" }
val TWO = new Val { def method = "2" }
val THREE = new Val { def method = "3" }
}
由於使用了匿名類,似乎很難擺脫'新'。 如果有人知道怎么做,請告訴我:)
答案,告訴Scala枚舉不支持args /方法 - 自定義值似乎是錯誤的。 其他答案(其中一些涉及implicit
)表明它可以做,但它們創建需要名稱重復的印象:您的值已聲明名稱為java對象字段,其次,名稱作為字符串提供給值構造函數而整點Enums的目的是創建一個可迭代的name-> value map,scala可以不用冗余:
object Ops1 extends Enumeration {
protected case class OpsVal(f: Int => Int) extends super.Val(/*nextId*/)
val ZERO = new FuncVal (x => 0)
val DOUBLE = new FuncVal (x => 2 * x )
implicit def convert(v: Value) = v.asInstanceOf[OpsVal]
}
// implicit is not needed
Ops1.ZERO.f(1) //> res0: Int = 0
// implicit is needed
Ops1.values map (v => (v + "=>" + v.f(1)))//> res1: scala.collection.immutable.SortedSet[String] = TreeSet(DOUBLE=>2, ZERO=>0)
我認為上面的內容比簡潔更簡潔
object Ops2 extends Enumeration {
protected abstract class OpsVal extends Val() {
def f(a: Int): Int
}
val ZERO = new OpsVal { def f(x: Int) = 0 }
val DOUBLE = new OpsVal { def f(x: Int) = 2 * x }
implicit def convert(valu: Value) = valu.asInstanceOf[OpsVal]
}
Ops2.ZERO.f(1) // implicit is not needed //> res2: Int = 0
// implicit is needed
Ops2_3.values map (v => (v, v(1))) //> res7: scala.collection.immutable.SortedSet[(e.Ops2_3.Value, Int)] = TreeSet
//| ((ZERO,0), (DOUBLE,2))
由於每個值只有一個方法,我們可以將它們轉換為函數
object Ops2_3 extends Enumeration {
protected case class FuncVal(f: Int => Int) extends Val {
def apply(x: Int) = f(x) // no need to extend Function1 explicitly
}
val ZERO = new FuncVal (x => 0)
val DOUBLE = new FuncVal (x => 2 * x )
implicit def convert(v: Value) = v.asInstanceOf[FuncVal]
}
Ops2_3.ZERO(1) // implicit is not needed //> res6: Int = 0
// implicit is needed
Ops2_3.values map (v => (v, v(1))) //> res7: scala.collection.immutable.SortedSet[(e.Ops2_3.Value, Int)] = TreeSet
//| ((ZERO,0), (DOUBLE,2))
在所有值之間共享的函數可以像這樣定義(在arg解析器中可用)
val args: Array[String] = "-silent -samples 100 -silent ".split(" +").toArray
//> args : Array[String] = Array(-silent, -samples, 100, -silent)
object Opts extends Enumeration {
val nopar, silent, samples = new Val() {
def apply() = args.contains(toString)
def asInt(default: Int) = { val i = args.indexOf(toString) ; if (i == -1) default else args(i+1).toInt}
def asInt: Int = asInt(-1)
override def toString = "-" + super.toString
}
}
Opts.nopar() //> res0: Boolean = false
Opts.samples.asInt //> res1: Int = 100
其他用戶爭論密封特征+宏的情況, 在Scala的密封特征上進行迭代?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.