您的位置:首页 > 其它

Scala:Enumeration

2015-12-03 11:29 423 查看
Scala开篇(目录)

先开看一下如何声明一个枚举对象

object EnumTest extends Enumeration{
type EnumTest = Value
val One,Two,Three = Value
}


这和我们在Java中声明有很大区别,Scala的枚举值有些特殊,它的关键是内部有一个Value类,所谓的枚举值都是通过它产生的。

如果我们不做任何约定的话,枚举值默认从0开始,依次+1

下面看一下Value类是怎么工作的。

val One,Two,Three = Value


上面代码中,分别对三个枚举元素执行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)


当我们编写 val One = Value 时, 调用的是Value的无参接口

protected final def Value: Value = Value(nextId)


上面的无参接口继续调用下面方法,nextId初始值是 0 ,每次构造Value对象时,自动在参数 i 的基础上加1,所以也就是我们可以为每个元素指定它的值的原因,第一个元素可以是0,也可以是其它的值,第二个元素不一定就是第一个元素+1,也可以指定任意的值

protected final def Value(i: Int): Value = Value(i, nextNameOrNull)


上面代码中我们看到 nextNameOrNull ,也就是调用了下面的接口,我们是可以为元素起名字的

protected final def Value(i: Int, name: String): Value = new Val(i, name)


我们是可以在子类中直接调用上面接口,对元素命名和指定初始值的

object EnumTest extends Enumeration{
type EnumTest = Value
val One,Two,Three = Value
val Four = Value(10,"four")
}


当我们为元素命名后,就可以通过名字来找到这个枚举元素了

println(EnumTest.withName("four").id)   // 输出:10


上面提到的四个方法,最终都会调动最后一个双参数的接口,来产生枚举值,这里就会提到另一个成员 Val

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

assert(!vmap.isDefinedAt(i), "Duplicate id: " + i)
vmap(i) = this
vsetDefined = false
//这里对nextId在i的基础上递增
nextId = i + 1
if (nextId > topId) topId = nextId
//如果你给的值,比最后一个元素的值都大,那最后一个值是i
if (i < bottomId) bottomId = i
def id = i
/**当我们访问其中的元素时,会调用这个方法
比如:println(EnumTest.One)
*/
override def toString() =
//命过名的,就取name,没有就用nameOf产生
if (name != null) name
else try thisenum.nameOf(i)
catch { case _: NoSuchElementException => "<Invalid enum: no field for #" + i + ">" }

protected def readResolve(): AnyRef = {
val enum = thisenum.readResolve().asInstanceOf[Enumeration]
if (enum.vmap == null) this
else enum.vmap(i)
}
}


Enumeration中有一个nmap对象,它里面保存着id和name的对应关系

private val nmap: mutable.Map[Int, String] = new mutable.HashMap


如果没有为元素命名,那么将会返回这个元素的字面内容,是通过populateNameMap方法设置的

private def populateNameMap() {
/**反射,取出所有声明的字段*/
val fields = getClass.getDeclaredFields
//进行判断,名字相符并且与返回类型相符
def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType)

// The list of possible Value methods: 0-args which return a conforming type
val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty &&
classOf[Value].isAssignableFrom(m.getReturnType) &&
m.getDeclaringClass != classOf[Enumeration] &&
isValDef(m))
//循环添加到nmap中
methods foreach { m =>
val name = m.getName
// invoke method to obtain actual `Value` instance
val value = m.invoke(this).asInstanceOf[Value]
// verify that outer points to the correct Enumeration: ticket #3616.
if (value.outerEnum eq thisenum) {
val id = Int.unbox(classOf[Val] getMethod "id" invoke value)
nmap += ((id, name))
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scala