Scala 中的枚举

Scala中的枚举

Scala不用关注枚举的特别语法,取而代之的是标准库中的类:scala.Enumeration

object Main extends App {

  object WeekDay extends Enumeration {
    type WeekDay = Value//这里仅仅是为了将Enumration.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//使用语法糖进行输出
}

上述是源码中给的一个枚举Example,可以发现:

  • 它是一个伴随对象
  • 需要显式得继承Enumeration
  • 具体的枚举实例都是需要赋值成Value这个对象(它是在超类中定义)

Enumeration源码分析

构造函数以有变量

/** Defines a finite set of values specific to the enumeration. Typically
 *  these values enumerate all possible forms something can take and provide
 *  a lightweight alternative to case classes.
 *
 *  Each call to a `Value` method adds a new unique value to the enumeration.
 *  To be accessible, these values are usually defined as `val` members of
 *  the evaluation.
 *
 *  All values in an enumeration share a common, unique type defined as the
 *  `Value` type member of the enumeration (`Value` selected on the stable
 *  identifier path of the enumeration instance).
 *
 *  @param initial The initial value from which to count the integers that
 *                 identifies values at run-time.
 *  @author  Matthias Zenger
 */
@SerialVersionUID(8476000850333817230L)
abstract class Enumeration (initial: Int) extends Serializable {
  thisenum =>

  def this() = this(0)

    /** The mapping from the integer used to identify values to the actual
    * values. */
  private val vmap: mutable.Map[Int, Value] = new mutable.HashMap

  /** The cache listing all values of this enumeration. */
  @transient private var vset: ValueSet = null
  @transient @volatile private var vsetDefined = false

  /** The mapping from the integer used to identify values to their
    * names. */
  private val nmap: mutable.Map[Int, String] = new mutable.HashMap

  /** The values of this enumeration as a set.
   */
  def values: ValueSet = {
    if (!vsetDefined) {
      vset = (ValueSet.newBuilder ++= vmap.values).result()
      vsetDefined = true
    }
    vset
  }

  /** The integer to use to identify the next created value. */
  protected var nextId: Int = initial

  /** The string to use to name the next created value. */
  protected var nextName: Iterator[String] = _

  private def nextNameOrNull =
    if (nextName != null && nextName.hasNext) nextName.next else null

  /** The highest integer amongst those used to identify values in this
    * enumeration. */
  private var topId = initial

  /** The lowest integer amongst those used to identify values in this
    * enumeration, but no higher than 0. */
  private var bottomId = if(initial < 0) initial else 0

  /** The one higher than the highest integer amongst those used to identify
    *  values in this enumeration. */
  final def maxId = topId

上面是洋洋洒洒贴了Enumeration类在Scala中的部分源码,从中大致可以看到这么几个关键点:

  • 它提供了一个轻量级的枚举类
  • initial初始化变量为整个枚举实例的容量,但是它在运行时可以变化
  • vmap:非常重要的一个变量,它是存储了枚举id和枚举值之间的映射容器。
  • nmap:也是蛮重要的一个变量,存储了枚举id和枚举名称的容器
  • **Id:所有带Id后缀的变量都是为了维护整个枚举的序号
  • 其他就是提供了各种迭代器,用于取值的东西

Value类的相关定义

/** The type of the enumerated values. */
@SerialVersionUID(7091335633555234129L)
abstract class Value extends Ordered[Value] with Serializable {
/** the id and bit location of this enumeration value */
def id: Int
/** a marker so we can tell whose values belong to whom come reflective-naming time */
private[Enumeration] val outerEnum = thisenum

override def compare(that: Value): Int =
  if (this.id < that.id) -1
  else if (this.id == that.id) 0
  else 1
override def equals(other: Any) = other match {
  case that: Enumeration#Value  => (outerEnum eq that.outerEnum) && (id == that.id)
  case _                        => false
}
override def hashCode: Int = id.##

/** Create a ValueSet which contains this value and another one */
def + (v: Value) = ValueSet(this, v)
}

在源码的Example中可以看到所有的枚举都是被赋值为Value类型,从上面的源码中可以看到Value类是一个抽象类,看它的内容只是提供了枚举值比较以及id的获取,在Enumeration源码中提供了一个叫Val的类对Value实现,重要是重写了整数命名和id的识别。

/** A class implementing the [[scala.Enumeration.Value]] type. This class
*  can be overridden to change the enumeration's naming and integer
*  identification behaviour.
*/
@SerialVersionUID(0 - 3501153230598116017L)
protected class Val(i: Int, name: String) extends Value with Serializable {
def this(i: Int)       = this(i, nextNameOrNull)
def this(name: String) = this(nextId, name)
def this()             = this(nextId)
//to do many things.
}

Value方法的定义

请注意,请注意,Example中的第4行、第5行的Value,前者是类,后者是方法(括号省略了而已)

/** Creates a fresh value, part of this enumeration. */
protected final def Value: Value = Value(nextId)

/** Creates a fresh value, part of this enumeration, identified by the
*  integer `i`.
*
*  @param i An integer that identifies this value at run-time. It must be
*           unique amongst all values of the enumeration.
*  @return  Fresh value identified by `i`.
*/
protected final def Value(i: Int): Value = Value(i, nextNameOrNull)

/** Creates a fresh value, part of this enumeration, called `name`.
*
*  @param name A human-readable name for that value.
*  @return  Fresh value called `name`.
*/
protected final def Value(name: String): Value = Value(nextId, name)

/** Creates a fresh value, part of this enumeration, called `name`
*  and identified by the integer `i`.
*
* @param i    An integer that identifies this value at run-time. It must be
*             unique amongst all values of the enumeration.
* @param name A human-readable name for that value.
* @return     Fresh value with the provided identifier `i` and name `name`.
*/
protected final def Value(i: Int, name: String): Value = new Val(i, name)

上面提供了各种Value的重载方法均是返回了Val类,所以说嘛,你有需要完全可以自己再实现一个Value类。

Scala中枚举的简单使用

println(WeekDay.Wed)//直接取枚举值
println(WeekDay.Wed.id)//取枚举值所在序号
println(WeekDay.maxId)//枚举值的个数
println(WeekDay.withName("Wed"))//通过字符串获取枚举(这里是不需要反射的)

获取枚举的具体信息还是非常方便的

Wed
2
7
Wed

获取全部的枚举值进行输出

WeekDay.values.foreach(println(_))
Mon
Tue
Wed
Thu
Fri
Sat
Sun

枚举也可以用在匹配

matchTest(WeekDay.Wed)
matchTest(WeekDay.Sat)

def matchTest=(week:WeekDay.Value) =>week match{//注意,这里枚举的类型都是Value
case w if w.compare(WeekDay.Fri)<=0 =>println("sorry,please working")
case WeekDay.Sat=>println(("go shopping"))
case WeekDay.Sun=>println(("sleeping"))
}

可以看到输出

sorry,please working
go shopping

这里的枚举值的比较其实就是对比他们的Id是否相等。

总结

  • Java中的那套枚举并不能直接使用到Scala中
  • Scala中的枚举使用轻量级Enumeration进行实现
  • Scala中的枚举其实是一个伴随对象
  • Scala中的枚举没有方法重写功能
  • Scala中的枚举其实都是Enumeration.Value这个对象

参考

http://www.cnblogs.com/gnivor/p/4191017.html
Enumeration.scala

原文

https://toutiao.io/posts/g9ktuv/preview

你可能感兴趣的:(Scala,scala,开发语言)