当前位置:  开发笔记 > 编程语言 > 正文

如何建模类型安全的枚举类型?

如何解决《如何建模类型安全的枚举类型?》经验,为你挑选了6个好方法。

Scala没有enum像Java那样的类型安全.给定一组相关常量,Scala表示这些常量的最佳方法是什么?



1> oxbow_lakes..:

我必须说上面的skaffman 从Scala文档中复制的示例在实践中的实用性有限(你可能也使用s).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


@macias`valueOf`的替换是`withName`,它不返回Option,如果没有匹配则抛出NSE.什么了!
顺便说一句.valueOf方法现在死了:-(
@Bluu你可以自己添加value:def valueOf(name:String)= WeekDay.values.find(_.toString == name)有一个选项

2> skaffman..:

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
  }


说真的,不应该使用申请.它没有固定; 引入了一个新的类App,它没有Schildmeijer提到的问题."object foo extends App {...}"也是如此.您可以通过args变量立即访问命令行参数.

3> Daniel C. So..:

有很多方法可以做.

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

这个答案基本上是我博客中本文的选定部分.



4> Walter Chang..:

一种稍微冗长的声明命名枚举的方式:

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> ron..:

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

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

}


"密封特征+案例对象"模式存在我在StackOverflow答案中详细说明的问题.但是,我确实弄清楚如何解决与此模式相关的所有问题,该问题也包含在该主题中:http://stackoverflow.com/a/25923651/501113

6> 小智..:

刚刚发现了enumeratum.这真是太神奇了,同样令人惊叹的是它并不为人所知!

推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有