在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中这样做?
编辑/有用的链接:
答案 0 :(得分:34)
以下是通过扩展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)
答案 1 :(得分:30)
在Chris' solution的基础上,您可以通过隐式转换实现更好的语法:
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
。
答案 2 :(得分:12)
Scala enumerations与Java枚举不同。
目前,没有办法添加方法(以理智的方式)。有一些解决方法,但在所有情况下和的任何内容都不像语法垃圾。
我尝试了类似的东西(向枚举的类实例添加方法,同时能够在运行时创建新实例,并在object
和new
实例之间具有工作等效关系。但是被bug #4023阻止了(“getClasses / getDeclaredClasses似乎错过了某些(REPL)或所有(scalac)类(对象)声明的”)。
由我来看看这些相关问题:
老实说,我不会使用Enumeration
。这是一个源自Scala 1.0(2004)的类,它有很奇怪的东西,并没有很多人(除了那些编写它的人)在没有教程的情况下理解如何使用它。
如果我绝对需要枚举,我会用Java编写该类。
答案 3 :(得分:11)
如果你不需要迭代枚举值或做一些其他的枚举,我建议使用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
表达式中的一个或多个案例时,编译器会发出警告。
答案 4 :(得分:8)
使用Aaron's solution在Scala 2.10中使用implicit classes(一种更紧凑的形式)进行阐述:
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
答案 5 :(得分:7)
你可以这样做:
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)
答案 6 :(得分:4)
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
*/
}
}
答案 7 :(得分:3)
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")
}
}
}
答案 8 :(得分:1)
在查看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" }
}
由于使用了匿名类,因此似乎很难摆脱'new'。 如果有人知道怎么做,请告诉我:)。
答案 9 :(得分:1)
如果你绝对需要每个枚举值的方法和需要能够迭代这些值,你可以这样做:
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之外,这些代码无法转移到更健全的枚举策略。
答案 10 :(得分:0)
answer, which tells that Scala enums do not support args/methods-customized values似乎错了。其他答案(其中一些涉及implicit
)证明它可以做,但它们创建了需要名称重复的印象:您的值已将名称声明为java对象字段,其次,该名称作为字符串提供给值构造函数而Enums的重点是创建一个可迭代的名称 - &gt;值映射,而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 parser中使用)
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
其他用户主张密封特征+宏的情况,Iteration over a sealed trait in Scala?