先开看一下如何声明一个枚举对象
<code class="hljs scala has-numbering"><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">EnumTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Enumeration</span>{</span> <span class="hljs-keyword">type</span> EnumTest = Value <span class="hljs-keyword">val</span> One,Two,Three = Value }</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>
这和我们在Java中声明有很大区别,Scala的枚举值有些特殊,它的关键是内部有一个Value类,所谓的枚举值都是通过它产生的。
如果我们不做任何约定的话,枚举值默认从0开始,依次+1
下面看一下Value类是怎么工作的。
<code class="hljs fix has-numbering"><span class="hljs-attribute">val One,Two,Three </span>=<span class="hljs-string"> Value</span></code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
上面代码中,分别对三个枚举元素执行Value方法,它有四个重载,它们会依次调用,源代码如下
<code class="hljs java has-numbering"> <span class="hljs-javadoc">/** Creates a fresh value, part of this enumeration. */</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> def Value: Value = Value(nextId) <span class="hljs-javadoc">/** Creates a fresh value, part of this enumeration, identified by the * integer `i`. * * <span class="hljs-javadoctag"> @param</span> i An integer that identifies this value at run-time. It must be * unique amongst all values of the enumeration. * <span class="hljs-javadoctag"> @return</span> Fresh value identified by `i`. */</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> def <span class="hljs-title">Value</span>(i: Int): Value = Value(i, nextNameOrNull) <span class="hljs-javadoc">/** Creates a fresh value, part of this enumeration, called `name`. * * <span class="hljs-javadoctag"> @param</span> name A human-readable name for that value. * <span class="hljs-javadoctag"> @return</span> Fresh value called `name`. */</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> def <span class="hljs-title">Value</span>(name: String): Value = Value(nextId, name) <span class="hljs-javadoc">/** Creates a fresh value, part of this enumeration, called `name` * and identified by the integer `i`. * *<span class="hljs-javadoctag"> @param</span> i An integer that identifies this value at run-time. It must be * unique amongst all values of the enumeration. *<span class="hljs-javadoctag"> @param</span> name A human-readable name for that value. *<span class="hljs-javadoctag"> @return</span> Fresh value with the provided identifier `i` and name `name`. */</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> def <span class="hljs-title">Value</span>(i: Int, name: String): Value = <span class="hljs-keyword">new</span> Val(i, name)</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li></ul>
当我们编写 val One = Value 时, 调用的是Value的无参接口
<code class="hljs python has-numbering">protected final <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Value</span>:</span> Value = Value(nextId)</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
上面的无参接口继续调用下面方法,nextId初始值是 0 ,每次构造Value对象时,自动在参数 i 的基础上加1,所以也就是我们可以为每个元素指定它的值的原因,第一个元素可以是0,也可以是其它的值,第二个元素不一定就是第一个元素+1,也可以指定任意的值
<code class="hljs python has-numbering">protected final <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Value</span><span class="hljs-params">(i: Int)</span>:</span> Value = Value(i, nextNameOrNull)</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
上面代码中我们看到 nextNameOrNull ,也就是调用了下面的接口,我们是可以为元素起名字的
<code class="hljs python has-numbering">protected final <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">Value</span><span class="hljs-params">(i: Int, name: String)</span>:</span> Value = new Val(i, name)</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
我们是可以在子类中直接调用上面接口,对元素命名和指定初始值的
<code class="hljs scala has-numbering"><span class="hljs-class"><span class="hljs-keyword">object</span> <span class="hljs-title">EnumTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Enumeration</span>{</span> <span class="hljs-keyword">type</span> EnumTest = Value <span class="hljs-keyword">val</span> One,Two,Three = Value <span class="hljs-keyword">val</span> Four = Value(<span class="hljs-number">10</span>,<span class="hljs-string">"four"</span>) }</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>
当我们为元素命名后,就可以通过名字来找到这个枚举元素了
<code class="hljs scss has-numbering"><span class="hljs-function">println(EnumTest.<span class="hljs-function">withName(<span class="hljs-string">"four"</span>)</span>.id)</span> <span class="hljs-comment">// 输出:10</span></code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
上面提到的四个方法,最终都会调动最后一个双参数的接口,来产生枚举值,这里就会提到另一个成员 Val
<code class="hljs scala has-numbering"> <span class="hljs-annotation">@SerialVersionUID</span>(<span class="hljs-number">0</span> - <span class="hljs-number">3501153230598116017</span>L) <span class="hljs-keyword">protected</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Val</span><span class="hljs-params">(i: Int, name: String)</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Value</span> <span class="hljs-keyword">with</span> <span class="hljs-title">Serializable</span> {</span> <span class="hljs-keyword">def</span> <span class="hljs-keyword">this</span>(i: Int) = <span class="hljs-keyword">this</span>(i, nextNameOrNull) <span class="hljs-keyword">def</span> <span class="hljs-keyword">this</span>(name: String) = <span class="hljs-keyword">this</span>(nextId, name) <span class="hljs-keyword">def</span> <span class="hljs-keyword">this</span>() = <span class="hljs-keyword">this</span>(nextId) assert(!vmap.isDefinedAt(i), <span class="hljs-string">"Duplicate id: "</span> + i) vmap(i) = <span class="hljs-keyword">this</span> vsetDefined = <span class="hljs-keyword">false</span> <span class="hljs-comment">//这里对nextId在i的基础上递增</span> nextId = i + <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> (nextId > topId) topId = nextId <span class="hljs-comment">//如果你给的值,比最后一个元素的值都大,那最后一个值是i</span> <span class="hljs-keyword">if</span> (i < bottomId) bottomId = i <span class="hljs-keyword">def</span> id = i <span class="hljs-javadoc">/**当我们访问其中的元素时,会调用这个方法 比如:println(EnumTest.One) */</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">def</span> toString() = <span class="hljs-comment">//命过名的,就取name,没有就用nameOf产生</span> <span class="hljs-keyword">if</span> (name != <span class="hljs-keyword">null</span>) name <span class="hljs-keyword">else</span> <span class="hljs-keyword">try</span> thisenum.nameOf(i) <span class="hljs-keyword">catch</span> { <span class="hljs-keyword">case</span> _: NoSuchElementException => <span class="hljs-string">"<Invalid enum: no field for #"</span> + i + <span class="hljs-string">">"</span> } <span class="hljs-keyword">protected</span> <span class="hljs-keyword">def</span> readResolve(): AnyRef = { <span class="hljs-keyword">val</span> enum = thisenum.readResolve().asInstanceOf[Enumeration] <span class="hljs-keyword">if</span> (enum.vmap == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">this</span> <span class="hljs-keyword">else</span> enum.vmap(i) } }</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul>
Enumeration中有一个nmap对象,它里面保存着id和name的对应关系
<code class="hljs fsharp has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> nmap: <span class="hljs-keyword">mutable</span>.Map[Int, String] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">mutable</span>.HashMap</code><ul style="opacity: 0.0422306;" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
如果没有为元素命名,那么将会返回这个元素的字面内容,是通过populateNameMap方法设置的
<code class="hljs scala has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">def</span> populateNameMap() { <span class="hljs-javadoc">/**反射,取出所有声明的字段*/</span> <span class="hljs-keyword">val</span> fields = getClass.getDeclaredFields <span class="hljs-comment">//进行判断,名字相符并且与返回类型相符</span> <span class="hljs-keyword">def</span> isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) <span class="hljs-comment">// The list of possible Value methods: 0-args which return a conforming type</span> <span class="hljs-keyword">val</span> methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && classOf[Value].isAssignableFrom(m.getReturnType) && m.getDeclaringClass != classOf[Enumeration] && isValDef(m)) <span class="hljs-comment">//循环添加到nmap中</span> methods foreach { m => <span class="hljs-keyword">val</span> name = m.getName <span class="hljs-comment">// invoke method to obtain actual `Value` instance</span> <span class="hljs-keyword">val</span> value = m.invoke(<span class="hljs-keyword">this</span>).asInstanceOf[Value] <span class="hljs-comment">// verify that outer points to the correct Enumeration: ticket #3616.</span> <span class="hljs-keyword">if</span> (value.outerEnum eq thisenum) { <span class="hljs-keyword">val</span> id = Int.unbox(classOf[Val] getMethod <span class="hljs-string">"id"</span> invoke value) nmap += ((id, name)) } } }</code>