如何建模類型安全的枚舉類型?

Scala沒有像Java那樣的類型安全enum 。 給定一組相關常量,Scala表示這些常量的最佳方法是什麼? html


#1樓

您可使用密封的抽象類而不是枚舉,例如: 數據庫

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))
  }

}

#2樓

http://www.scala-lang.org/docu/files/api/scala/Enumeration.html api

使用示例 安全

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
  }

#3樓

我必須說上面的skaffman 從Scala文檔中複製的示例在實踐中的實用性有限(你可能也使用case object )。 lua

爲了得到最接近Java Enum東西(即便用合理的toStringvalueOf方法 - 也許你將枚舉值保存到數據庫中),你須要稍微修改一下。 若是您使用過skaffman的代碼: spa

WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString   //returns Weekday(2)

而使用如下聲明: scala

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon = Value("Mon")
  val Tue = Value("Tue") 
  ... etc
}

你會獲得更明智的結果: code

WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString   //returns Tue

#4樓

一種稍微冗長的聲明命名枚舉的方式: htm

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,這將更容易。 對象


#5樓

有不少方法能夠作。

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)
<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編譯器就不會警告我。 因此它給了我一些類型的安全性,但沒有獲得多少。

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等。

這個答案基本上是我博客中本文的選定部分。

相關文章
相關標籤/搜索