object EnumTest extends Enumeration {
type EnumTest = Value // 類型別名,方便外部引用
val One, Two, Three = Value
}
與 Java enum 不同,Scala 枚舉是通過繼承 Enumeration 類、調用 Value 方法生成實例實現的。
調用 val One = Value 時,依次觸發:
// 1. 無參版本 protected final def Value: Value = Value(nextId) // 2. 僅指定 id protected final def Value(i: Int): Value = Value(i, nextNameOrNull) // 3. 僅指定名稱 protected final def Value(name: String): Value = Value(nextId, name) // 4. 完整版本:指定 id 和名稱 protected final def Value(i: Int, name: String): Value = new Val(i, name)
nextId:自動遞增計數器,初始 0nextNameOrNull:通過反射獲取字段名作為名稱object EnumTest extends Enumeration {
type EnumTest = Value
val One, Two, Three = Value
val Four = Value(10, "four") // 指定 id=10,名稱="four"
}
// 使用
println(EnumTest.withName("four").id) // 輸出 10
println(EnumTest.Four) // 輸出 four(toString 返回名稱)
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 // id -> Val 映射
vsetDefined = false
nextId = i + 1 // 關鍵:下一個自動 id 基於當前 i
if (nextId > topId) topId = nextId
if (i < bottomId) bottomId = i
def id = i
override def toString() =
if (name != null) name
else try thisenum.nameOf(i) catch { case _ => "" }
protected def readResolve(): AnyRef = { ... } // 序列化支持
}
val A = Value(5),則下一個無參 Value 的 id 為 6vmap: Map[Int, Val](id→實例),nmap: Map[Int, String](id→名稱,通過反射填充)populateNameMap() 利用反射掃描所有無參返回 Value 的方法,構建 id→字段名映射private def populateNameMap() {
val fields = getClass.getDeclaredFields
def isValDef(m: JMethod) = fields exists (fd =>
fd.getName == m.getName && fd.getType == m.getReturnType)
val methods = getClass.getMethods filter (m =>
m.getParameterTypes.isEmpty &&
classOf[Value].isAssignableFrom(m.getReturnType) &&
m.getDeclaringClass != classOf[Enumeration] &&
isValDef(m))
methods foreach { m =>
val name = m.getName
val value = m.invoke(this).asInstanceOf[Value]
if (value.outerEnum eq thisenum) {
val id = Int.unbox(classOf[Val].getMethod("id").invoke(value))
nmap += ((id, name))
}
}
}
| 操作 | 代碼 | 說明 |
|---|---|---|
| 遍歷所有值 | EnumTest.values.foreach(println) | 返回 ValueSet(有序集合) |
| 按 id 獲取 | EnumTest(10) 或 EnumTest.apply(10) | 返回對應 Val,不存在拋出 NoSuchElementException |
| 按名稱獲取 | EnumTest.withName("four") | 區分大小寫,不存在拋出異常 |
| 獲取 id | EnumTest.One.id | 返回 Int |
| 比較 | EnumTest.One == EnumTest.Two | 比較 id |
| 模式匹配 | case EnumTest.One => ... | 支援 switch 風格 |
Enumeration 的子類對象;Java enum 是編譯器生成的 Enum 子類type MyEnum = Value 導出類型別名;Java 直接使用枚舉類名enum 關鍵字)實現類型安全枚舉,避免反射開銷Enumeration 仍是可行選擇withName(反射查找),預先構建名稱映射緩存