Scala没有enum
像Java那样的类型安全.给定一组相关常量,Scala表示这些常量的最佳方法是什么?
我必须说上面的skaffman 从Scala文档中复制的示例在实践中的实用性有限(你可能也使用s).case object
为了获得最接近Java的东西Enum
(即使用合理的方法toString
和valueOf
方法 - 也许你将枚举值保存到数据库中),你需要稍微修改一下.如果您使用过skaffman的代码:
WeekDay.valueOf("Sun") //returns None WeekDay.Tue.toString //returns Weekday(2)
而使用以下声明:
object WeekDay extends Enumeration { type WeekDay = Value val Mon = Value("Mon") val Tue = Value("Tue") ... etc }
你会得到更明智的结果:
WeekDay.valueOf("Sun") //returns Some(Sun) WeekDay.Tue.toString //returns Tue
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
使用示例
object Main extends App { object WeekDay extends Enumeration { type WeekDay = Value val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value } import WeekDay._ def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) WeekDay.values filter isWorkingDay foreach println }
有很多方法可以做.
1)使用符号.但是,除了不接受符号所在的非符号之外,它不会给你任何类型安全.我只是在这里提到完整性.这是一个用法示例:
def update(what: Symbol, where: Int, newValue: Array[Int]): MatrixInt = what match { case 'row => replaceRow(where, newValue) case 'col | 'column => replaceCol(where, newValue) case _ => throw new IllegalArgumentException } // At REPL: scala> val a = unitMatrixInt(3) a: teste7.MatrixInt = / 1 0 0 \ | 0 1 0 | \ 0 0 1 / scala> a('row, 1) = a.row(0) res41: teste7.MatrixInt = / 1 0 0 \ | 1 0 0 | \ 0 0 1 / scala> a('column, 2) = a.row(0) res42: teste7.MatrixInt = / 1 0 1 \ | 0 1 0 | \ 0 0 0 /
2)使用课程Enumeration
:
object Dimension extends Enumeration { type Dimension = Value val Row, Column = Value }
或者,如果您需要序列化或显示它:
object Dimension extends Enumeration("Row", "Column") { type Dimension = Value val Row, Column = Value }
这可以这样使用:
def update(what: Dimension, where: Int, newValue: Array[Int]): MatrixInt = what match { case Row => replaceRow(where, newValue) case Column => replaceCol(where, newValue) } // At REPL: scala> a(Row, 2) = a.row(1):13: error: not found: value Row a(Row, 2) = a.row(1) ^ scala> a(Dimension.Row, 2) = a.row(1) res1: teste.MatrixInt = / 1 0 0 \ | 0 1 0 | \ 0 1 0 / scala> import Dimension._ import Dimension._ scala> a(Row, 2) = a.row(1) res2: teste.MatrixInt = / 1 0 0 \ | 0 1 0 | \ 0 1 0 /
不幸的是,它并不能确保所有匹配都被考虑在内.如果我忘了将Row或Column放在匹配中,Scala编译器就不会警告我.所以它给了我一些类型的安全性,但没有得到多少.
3)案例对象:
sealed abstract class Dimension case object Row extends Dimension case object Column extends Dimension
现在,如果我在a上省略一个案例match
,编译器会警告我:
MatrixInt.scala:70: warning: match is not exhaustive! missing combination Column what match { ^ one warning found
它的使用方式几乎相同,甚至不需要import
:
scala> val a = unitMatrixInt(3) a: teste3.MatrixInt = / 1 0 0 \ | 0 1 0 | \ 0 0 1 / scala> a(Row,2) = a.row(0) res15: teste3.MatrixInt = / 1 0 0 \ | 0 1 0 | \ 1 0 0 /
那么,您可能想知道为什么要使用枚举而不是案例对象.事实上,案例对象确实具有很多次优势,例如这里.但是,Enumeration类有许多Collection方法,例如元素(Scala 2.8上的迭代器),它返回Iterator,map,flatMap,filter等.
这个答案基本上是我博客中本文的选定部分.
一种稍微冗长的声明命名枚举的方式:
object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") { type WeekDay = Value val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value } WeekDay.valueOf("Wed") // returns Some(Wed) WeekDay.Fri.toString // returns Fri
当然,这里的问题是你需要保持名称和val的顺序同步,如果在同一行上声明name和val,这将更容易.
您可以使用密封的抽象类而不是枚举,例如:
sealed abstract class Constraint(val name: String, val verifier: Int => Boolean) case object NotTooBig extends Constraint("NotTooBig", (_ < 1000)) case object NonZero extends Constraint("NonZero", (_ != 0)) case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x)) object Main { def eval(ctrs: Seq[Constraint])(x: Int): Boolean = (true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) } def main(args: Array[String]) { val ctrs = NotTooBig :: NotEquals(5) :: Nil val evaluate = eval(ctrs) _ println(evaluate(3000)) println(evaluate(3)) println(evaluate(5)) } }
刚刚发现了enumeratum.这真是太神奇了,同样令人惊叹的是它并不为人所知!