Scala:Enumeration
2015-12-03 11:29
423 查看
Scala开篇(目录)
先开看一下如何声明一个枚举对象
这和我们在Java中声明有很大区别,Scala的枚举值有些特殊,它的关键是内部有一个Value类,所谓的枚举值都是通过它产生的。
如果我们不做任何约定的话,枚举值默认从0开始,依次+1
下面看一下Value类是怎么工作的。
上面代码中,分别对三个枚举元素执行Value方法,它有四个重载,它们会依次调用,源代码如下
当我们编写 val One = Value 时, 调用的是Value的无参接口
上面的无参接口继续调用下面方法,nextId初始值是 0 ,每次构造Value对象时,自动在参数 i 的基础上加1,所以也就是我们可以为每个元素指定它的值的原因,第一个元素可以是0,也可以是其它的值,第二个元素不一定就是第一个元素+1,也可以指定任意的值
上面代码中我们看到 nextNameOrNull ,也就是调用了下面的接口,我们是可以为元素起名字的
我们是可以在子类中直接调用上面接口,对元素命名和指定初始值的
当我们为元素命名后,就可以通过名字来找到这个枚举元素了
上面提到的四个方法,最终都会调动最后一个双参数的接口,来产生枚举值,这里就会提到另一个成员 Val
Enumeration中有一个nmap对象,它里面保存着id和name的对应关系
如果没有为元素命名,那么将会返回这个元素的字面内容,是通过populateNameMap方法设置的
先开看一下如何声明一个枚举对象
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)) } } }
相关文章推荐
- Windows下Scala环境搭建
- Windows7下安装Scala 2.9.2教程
- XML 文件解析--含Unicode字符的XML文件
- 分分钟掌握快速排序(Java / Scala 实现)
- Scala极速入门
- Spark初探
- Scala实现REST操作
- Scala method call syntax
- 关于Scala多重继承的菱形问题
- Scala 高阶函数(high-order function)剖析
- Scala Monad Design Pattern
- Spray.io搭建Rest服务
- Spray.io搭建Rest — 支持Twirl模板并部署
- 搭建hadoop/spark集群环境
- Akka (actors) remote example
- scala工具库
- scala-协变、逆变、上界、下界
- scala-常用函数介绍
- zeppelin入门使用
- ScalaMP ---- 模仿 OpenMp 的一个简单并行计算框架