关于scala:如何为类型安全的枚举类型建模?

How to model type-safe enum types?

斯卡拉没有像Java一样具有类型安全的EDCOX1 0。给定一组相关的常量,scala中表示这些常量的最佳方法是什么?


我必须说的是,例如copied跳出Scala文档由skaffman以上在实践中的效用是有有限公司(你不妨使用case objects)。

为了得到一些最紧密resembling a Java Enum(IU与valueOftoString和敏感的方法在你的价值persisting ENUM数据库)的数据位的你需要的信息。如果你曾经使用skaffman的代码:

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

而使用以下的声明:

1
2
3
4
5
6
object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon = Value("Mon")
  val Tue = Value("Tue")
  ... etc
}

结果:你得到更多的敏感

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


www.scala-lang.org http:/ / / / /文件/文件/ enumeration.html Scala API

使用的例子

1
2
3
4
5
6
7
8
9
10
11
12
  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)使用的符号。它不会给你任何类型安全的,虽然,除了不接受非符号的符号是在普通。我在这里只提它的完备性。这是一个例子:在大学。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)

1
2
3
4
object Dimension extends Enumeration {
  type Dimension = Value
  val Row, Column = Value
}

或者,如果你需要序列化或显示它:

1
2
3
4
object Dimension extends Enumeration("Row","Column") {
  type Dimension = Value
  val Row, Column = Value
}

这可以用样本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 /

不幸的是,它不会是accounted确保所有比赛。如果我忘了把行或列中匹配,Scala编译器不会有警告我。所以它给我一些但不是类型安全的,多为可以得到。

3)对象:案例

1
2
3
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension

现在,如果我离开matchout on a a case,我想:编译器警告

1
2
3
4
5
6
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination         Column

    what match {
    ^
one warning found

它使用同样的方式相当多,甚至没有需要import

1
2
3
4
5
6
7
8
9
10
11
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 /

你可能知道,那么,为什么使用"案例"在枚举对象。作为一个事实上,个案对象做多次有优势,如在这里。类的枚举,但安切洛蒂在收集方法,如元素(迭代器在Scala 2.8),它返回的迭代器,地图,flatmap、过滤器等。

这个答案是选配件奖从这篇文章在我的博客。


不需要冗长的命名方式:declaring枚举

1
2
3
4
5
6
7
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是被在相同的线路。


你可以使用一个抽象类而不是密封的枚举,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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刚刚发现的。它的漂亮和惊人的惊人后,它更多的是众所周知的。


广泛的研究后,做一个"枚举"选项在Scala中,一贴一个更完整的概述本域的另一个线程上的计算器。它包括一个密封的解决方案数量+案例对象"模式在《我有我的JVM的类/对象初始化顺序问题。


在Scala中,它是非常舒适的github.com http:////enumeratum lloydmeta

一个项目是非常好的例子和文档

从这个例子就应该让你感兴趣的文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import enumeratum._

sealed trait Greeting extends EnumEntry

object Greeting extends Enum[Greeting] {

  /*
   `findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`

   You use it to implement the `val values` member
  */

  val values = findValues

  case object Hello   extends Greeting
  case object GoodBye extends Greeting
  case object Hi      extends Greeting
  case object Bye     extends Greeting

}

// Object Greeting has a `withName(name: String)` method
Greeting.withName("Hello")
// => res0: Greeting = Hello

Greeting.withName("Haro")
// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)

// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]
Greeting.withNameOption("Hello")
// => res1: Option[Greeting] = Some(Hello)

Greeting.withNameOption("Haro")
// => res2: Option[Greeting] = None

// It is also possible to use strings case insensitively
Greeting.withNameInsensitive("HeLLo")
// => res3: Greeting = Hello

Greeting.withNameInsensitiveOption("HeLLo")
// => res4: Option[Greeting] = Some(Hello)

// Uppercase-only strings may also be used
Greeting.withNameUppercaseOnly("HELLO")
// => res5: Greeting = Hello

Greeting.withNameUppercaseOnlyOption("HeLLo")
// => res6: Option[Greeting] = None

// Similarly, lowercase-only strings may also be used
Greeting.withNameLowercaseOnly("hello")
// => res7: Greeting = Hello

Greeting.withNameLowercaseOnlyOption("hello")
// => res8: Option[Greeting] = Some(Hello)

多蒂(Scala的枚举支持的有3人)。检查在这里和这里。