如何build模types安全的枚举types?

Scala没有像Java那样的types安全enum 。 给定一组相关的常量,Scala中表示这些常量的最好方法是什么?

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 } 

我必须说,上面skaffman 从Scala文档中复制出来的例子在实践中是有用的(你也可以使用case object )。

为了得到最类似于Java Enum东西(比如,明智的toStringvalueOf方法 – 也许你将枚举值保存到数据库中),你需要修改一下。 如果你使用过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 

有很多方法可以做。

1)使用符号。 但是,除了不接受符号所在的非符号外,它不会给你任何types的安全。 我只是在这里提到完整性。 以下是一个使用示例:

 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) <console>: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编译器就不会提醒我。 所以它给了我一些types的安全性,但不是可以获得的。

3)案例对象:

 sealed abstract class Dimension case object Row extends Dimension case object Column extends Dimension 

现在,如果我忽略了一个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等等

这个答案本质上是从我的博客这篇文章中select的部分。

声明命名枚举的方法稍微less一些:

 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在同一行中声明,则需要保持名称和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)) } } 

只是发现了枚举 。 这是相当惊人的,同样惊人,它不是更为人所知!

在对Scala中“枚举”的所有选项进行了广泛的研究之后,我在另一个StackOverflow线程上发布了更完整的关于这个域的概述。 它包含了解决JVM类/对象初始化sorting问题的“密封特质+案例对象”模式的解决scheme。