文章目录
  1. 1. 先谈Java中的枚举
  2. 2. 再看Scala中的枚举
  3. 3. Enumeration源码分析
    1. 3.1. 构造函数以有变量
    2. 3.2. Value类的相关定义
    3. 3.3. Value方法的定义
  4. 4. Scala中枚举的简单使用
  5. 5. 总结
  6. 6. 参考

先谈Java中的枚举

Scala的枚举之前,我们先来讲讲关于Java里面的枚举(相信对于广大程序猿来说都是很熟悉):

  • 使用enum关键词定义

    1
    public enum WeekDay{...}
  • 无法再继承其他类或者枚举(但是可以实现接口),因为它默认继承了java.lang.Enum

  • 无法在外部调用枚举的构造函数(因为枚举的构造函数都是私有的,只有内部才能调用)
  • 枚举其实是一组int常量(当初它就是因为使用int常量的需求很大才又了枚举),它还可以在swtich中很方便的使用
  • 每个枚举变量还可以重写枚举类或者实现接口中的方法(这个蛮好用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public enum WeekDay{
    Mon{
    @Override
    public String say(){return "monday";}
    },
    Tue{
    @Override
    public String say(){return "tuesday";}
    };

    public String say(){return "";}
    }
  • …(我也不知道了-_-)

再看Scala中的枚举

你会发现,上面说的一切在Scala中其实并没有甚么卵用-_-,因为在Scala中压根就么有enum这个关键词,但是当初发明Scala大神兼Java的创始人并没有落下枚举。

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

1
2
3
4
5
6
7
8
9
10
11
12
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源码分析

构造函数以有变量

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
55
56
57
58
59
60
61
62
63
64
/** 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类的相关定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** 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的识别。

1
2
3
4
5
6
7
8
9
10
11
/** 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,前者是类,后者是方法(括号省略了而已)

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
/** 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中枚举的简单使用

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

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

Wed
2
7
Wed

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

1
WeekDay.values.foreach(println(_))

Mon
Tue
Wed
Thu
Fri
Sat
Sun

枚举也可以用在匹配

1
2
3
4
5
6
7
8
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这个对象

参考


本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权kubiCode,并且,不得用于商业用途。如您有任何疑问或者授权方面的协商,请给我留言

文章目录
  1. 1. 先谈Java中的枚举
  2. 2. 再看Scala中的枚举
  3. 3. Enumeration源码分析
    1. 3.1. 构造函数以有变量
    2. 3.2. Value类的相关定义
    3. 3.3. Value方法的定义
  4. 4. Scala中枚举的简单使用
  5. 5. 总结
  6. 6. 参考