程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> Scala:Enumeration

Scala:Enumeration

編輯:關於C++

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))
      }
    }
  }
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved