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