Java 枚举:理解枚举本质

C 语言中可以这样来定义枚举

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. enum color {  
  2.         RED=0, GREEN, BLUE, YELLOW  
  3. } col;  

关键字 enum 定义枚举,在定义枚举的同时,声明该枚举变量 col.


注意:C 语言中枚举成员的值是根据上下文自动加 1 的(GREEN = 1, BLUE = 2 等)。


C 语言中 switch 语句支持枚举类型

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #include<stdio.h>  
  2. int main() {  
  3.     enum color {  
  4.         RED=0, GREEN, BLUE, YELLOW  
  5.     } col;  
  6.   
  7.     int cl;  
  8.   
  9.     printf("0=red,1=green,2=blue,3=yellow. seclect:\n");  
  10.     scanf("%d",&cl);  
  11.   
  12.     col=(enum color) cl;  
  13.   
  14.     switch(col) {  
  15.         case RED:  
  16.             printf("the color is red\n");  
  17.             break;  
  18.         case GREEN:  
  19.             printf("the color is green\n");  
  20.             break;  
  21.         case BLUE:  
  22.              printf("the color is blue\n");  
  23.             break;  
  24.         case YELLOW:  
  25.             printf("the color is yellow\n");  
  26.             break;  
  27.         defalut:  
  28.             printf("no this color\n");  
  29.             break;  
  30.     }  
  31.   
  32.     return 0;  
  33. }  

那么,Java 里面的枚举与其类似,但是又不是完全一样。


Java 语言中定义枚举也是使用关键字 enum

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.     RED, GREEN, BLUE, YELLOW;  
  3. }  

上述定义了一个枚举类型 Color(可以说是类,编译之后是 Color.class).


上面的定义,还可以改成下面的这种形式

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.     RED(), GREEN(), BLUE(), YELLOW();  
  3. }  

到这里你就会觉得迷茫(如果你是初学者的话),为什么这样子也可以?


其实,枚举的成员就是枚举对象,只不过他们是静态常量而已。


使用 javap 命令(javap 文件名<没有后缀.class>)可以反编译 class 文件,如下:



我们可以使用普通类来模拟枚举,下面定义一个 Color 类。

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public class Color {  
  2.     private static final Color RED = new Color();  
  3.     private static final Color GREEN = new Color();  
  4.     private static final Color BLUE = new Color();  
  5.     private static final Color YELLOW = new Color();  
  6. }  

对比一下,你就明白了。


如果按照这个逻辑,是否还可以为其添加另外的构造方法?答案是肯定的!

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.         RED("red color", 0), GREEN("green color", 1),   
  3.         BLUE("blue color", 2), YELLOW("yellow color", 3);  
  4.   
  5.         Color(String name, int id) {  
  6.             _name = name;  
  7.             _id = id;  
  8.         }  
  9.   
  10.         String _name;  
  11.         int _id;  
  12. }  

为 Color 声明了两个成员变量,并为其构造带参数的构造器。

如果你这样创建一个枚举

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.         RED("red color"0), GREEN("green color"1),   
  3.         BLUE("blue color"2), YELLOW("yellow color"3);  
  4. }  

编译器就会报错

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. The constructor EnumDemo.Color(String, int) is undefined  

到此,你就可以明白,枚举和普通类基本一致(但是不完全一样)。


对于类来讲,最好将其成员变量私有化,然后,为成员变量提供 get、set 方法。

按照这个原则,可以进一步写好 enum Color.

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.         RED("red color"0), GREEN("green color"1),  
  3.         BLUE("blue color"2), YELLOW("yellow color"3);  
  4.   
  5.         Color(String name, int id) {  
  6.             _name = name;  
  7.             _id = id;  
  8.         }  
  9.   
  10.         private String _name;  
  11.         private int _id;  
  12.           
  13.         public void setName(String name) {  
  14.             _name = name;  
  15.         }  
  16.           
  17.         public void setId(int id) {  
  18.             _id = id;  
  19.         }  
  20.   
  21.         public String getName() {  
  22.             return _name;  
  23.         }  
  24.   
  25.         public int getId() {  
  26.             return _id;  
  27.         }  
  28. }  


但是,java 设计 enum 的目的是提供一组常量,方便用户设计。

如果我们冒然的提供 set 方法(外界可以改变其成员属性),好像是有点违背了设计的初衷。

那么,我们应该舍弃 set 方法,保留 get 方法。

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public enum Color {  
  2.         RED("red color"0), GREEN("green color"1),  
  3.         BLUE("blue color"2), YELLOW("yellow color"3);  
  4.   
  5.         Color(String name, int id) {  
  6.             _name = name;  
  7.             _id = id;  
  8.         }  
  9.   
  10.         private String _name;  
  11.         private int _id;  
  12.           
  13.         public String getName() {  
  14.             return _name;  
  15.         }  
  16.   
  17.         public int getId() {  
  18.             return _id;  
  19.         }  
  20. }  

普通类,我们可以将其实例化,那么,能否实例化枚举呢?


在回答这个问题之前,先来看看,编译之后的 Color.class 文件

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static enum Color {  
  2.         RED("red color"0), GREEN("green color"1),  
  3.         BLUE("blue color"2), YELLOW("yellow color"3);  
  4.   
  5.         private String _name;  
  6.         private int _id;  
  7.   
  8.         private Color(String name, int id) {  
  9.             this._name = name;  
  10.             this._id = id;  
  11.         }  
  12.   
  13.         public String getName() {  
  14.             return this._name;  
  15.         }  
  16.   
  17.         public int getId() {  
  18.             return this._id;  
  19.         }  
  20. }  

可以看出,编译器淘气的为其构造方法加上了 private,那么也就是说,我们无法实例化枚举。


所有枚举类都继承了 Enum 类的方法,包括 toString 、equals、hashcode 等方法。

因为 equals、hashcode 方法是 final 的,所以不可以被枚举重写(只可以继承)。

但是,可以重写 toString 方法。


关于 Enum 源码,详见附录!


那么,使用 Java 的不同类来模拟一下枚举,大概是这个样子

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package mark.demo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. public class Color {  
  7.     private static final Color RED = new Color("red color"0);  
  8.     private static final Color GREEN = new Color("green color"1);  
  9.     private static final Color BLUE = new Color("blue color"2);  
  10.     private static final Color YELLOW = new Color("yellow color"3);  
  11.   
  12.     private final String _name;  
  13.     private final int _id;  
  14.   
  15.     private Color(String name, int id) {  
  16.         _name = name;  
  17.         _id = id;  
  18.     }  
  19.   
  20.     public String getName() {  
  21.         return _name;  
  22.     }  
  23.   
  24.     public int getId() {  
  25.         return _id;  
  26.     }  
  27.   
  28.     public static List<Color> values() {  
  29.         List<Color> list = new ArrayList<Color>();  
  30.         list.add(RED);  
  31.         list.add(GREEN);  
  32.         list.add(BLUE);  
  33.         list.add(YELLOW);  
  34.         return list;  
  35.     }  
  36.   
  37.     @Override  
  38.     public String toString() {  
  39.         return "the color _name=" + _name + ", _id=" + _id;  
  40.     }  
  41.   
  42. }  


附录


Enum.java

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * %W% %E% 
  3.  * 
  4.  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. 
  5.  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 
  6.  */  
  7.   
  8. package java.lang;  
  9.   
  10. import java.io.Serializable;  
  11. import java.io.IOException;  
  12. import java.io.InvalidObjectException;  
  13. import java.io.ObjectInputStream;  
  14. import java.io.ObjectStreamException;  
  15.   
  16. /** 
  17.  * This is the common base class of all Java language enumeration types. 
  18.  * 
  19.  * @author  Josh Bloch 
  20.  * @author  Neal Gafter 
  21.  * @version %I%, %G% 
  22.  * @since   1.5 
  23.  */  
  24. public abstract class Enum<E extends Enum<E>>  
  25.         implements Comparable<E>, Serializable {  
  26.     /** 
  27.      * The name of this enum constant, as declared in the enum declaration. 
  28.      * Most programmers should use the {@link #toString} method rather than 
  29.      * accessing this field. 
  30.      */  
  31.     private final String name;  
  32.   
  33.     /** 
  34.      * Returns the name of this enum constant, exactly as declared in its 
  35.      * enum declaration. 
  36.      *  
  37.      * <b>Most programmers should use the {@link #toString} method in 
  38.      * preference to this one, as the toString method may return 
  39.      * a more user-friendly name.</b>  This method is designed primarily for 
  40.      * use in specialized situations where correctness depends on getting the 
  41.      * exact name, which will not vary from release to release. 
  42.      * 
  43.      * @return the name of this enum constant 
  44.      */  
  45.     public final String name() {  
  46.     return name;  
  47.     }  
  48.   
  49.     /** 
  50.      * The ordinal of this enumeration constant (its position 
  51.      * in the enum declaration, where the initial constant is assigned 
  52.      * an ordinal of zero). 
  53.      *  
  54.      * Most programmers will have no use for this field.  It is designed 
  55.      * for use by sophisticated enum-based data structures, such as 
  56.      * {@link java.util.EnumSet} and {@link java.util.EnumMap}. 
  57.      */  
  58.     private final int ordinal;  
  59.   
  60.     /** 
  61.      * Returns the ordinal of this enumeration constant (its position 
  62.      * in its enum declaration, where the initial constant is assigned 
  63.      * an ordinal of zero). 
  64.      *  
  65.      * Most programmers will have no use for this method.  It is 
  66.      * designed for use by sophisticated enum-based data structures, such 
  67.      * as {@link java.util.EnumSet} and {@link java.util.EnumMap}. 
  68.      * 
  69.      * @return the ordinal of this enumeration constant 
  70.      */  
  71.     public final int ordinal() {  
  72.     return ordinal;  
  73.     }  
  74.   
  75.     /** 
  76.      * Sole constructor.  Programmers cannot invoke this constructor. 
  77.      * It is for use by code emitted by the compiler in response to 
  78.      * enum type declarations. 
  79.      * 
  80.      * @param name - The name of this enum constant, which is the identifier 
  81.      *               used to declare it. 
  82.      * @param ordinal - The ordinal of this enumeration constant (its position 
  83.      *         in the enum declaration, where the initial constant is assigned 
  84.      *         an ordinal of zero). 
  85.      */  
  86.     protected Enum(String name, int ordinal) {  
  87.     this.name = name;  
  88.     this.ordinal = ordinal;  
  89.     }  
  90.   
  91.     /** 
  92.      * Returns the name of this enum constant, as contained in the 
  93.      * declaration.  This method may be overridden, though it typically 
  94.      * isn't necessary or desirable.  An enum type should override this 
  95.      * method when a more "programmer-friendly" string form exists. 
  96.      * 
  97.      * @return the name of this enum constant 
  98.      */  
  99.     public String toString() {  
  100.     return name;  
  101.     }  
  102.   
  103.     /** 
  104.      * Returns true if the specified object is equal to this 
  105.      * enum constant. 
  106.      * 
  107.      * @param other the object to be compared for equality with this object. 
  108.      * @return  true if the specified object is equal to this 
  109.      *          enum constant. 
  110.      */  
  111.     public final boolean equals(Object other) {   
  112.         return this==other;  
  113.     }  
  114.   
  115.     /** 
  116.      * Returns a hash code for this enum constant. 
  117.      * 
  118.      * @return a hash code for this enum constant. 
  119.      */  
  120.     public final int hashCode() {  
  121.         return super.hashCode();  
  122.     }  
  123.   
  124.     /** 
  125.      * Throws CloneNotSupportedException.  This guarantees that enums 
  126.      * are never cloned, which is necessary to preserve their "singleton" 
  127.      * status. 
  128.      * 
  129.      * @return (never returns) 
  130.      */  
  131.     protected final Object clone() throws CloneNotSupportedException {  
  132.     throw new CloneNotSupportedException();  
  133.     }  
  134.   
  135.     /** 
  136.      * Compares this enum with the specified object for order.  Returns a 
  137.      * negative integer, zero, or a positive integer as this object is less 
  138.      * than, equal to, or greater than the specified object. 
  139.      *  
  140.      * Enum constants are only comparable to other enum constants of the 
  141.      * same enum type.  The natural order implemented by this 
  142.      * method is the order in which the constants are declared. 
  143.      */  
  144.     public final int compareTo(E o) {  
  145.     Enum other = (Enum)o;  
  146.     Enum self = this;  
  147.     if (self.getClass() != other.getClass() && // optimization  
  148.             self.getDeclaringClass() != other.getDeclaringClass())  
  149.         throw new ClassCastException();  
  150.     return self.ordinal - other.ordinal;  
  151.     }  
  152.   
  153.     /** 
  154.      * Returns the Class object corresponding to this enum constant's 
  155.      * enum type.  Two enum constants e1 and  e2 are of the 
  156.      * same enum type if and only if 
  157.      *   e1.getDeclaringClass() == e2.getDeclaringClass(). 
  158.      * (The value returned by this method may differ from the one returned 
  159.      * by the {@link Object#getClass} method for enum constants with 
  160.      * constant-specific class bodies.) 
  161.      * 
  162.      * @return the Class object corresponding to this enum constant's 
  163.      *     enum type 
  164.      */  
  165.     public final Class<E> getDeclaringClass() {  
  166.     Class clazz = getClass();  
  167.     Class zuper = clazz.getSuperclass();  
  168.     return (zuper == Enum.class) ? clazz : zuper;  
  169.     }  
  170.   
  171.     /** 
  172.      * Returns the enum constant of the specified enum type with the 
  173.      * specified name.  The name must match exactly an identifier used 
  174.      * to declare an enum constant in this type.  (Extraneous whitespace 
  175.      * characters are not permitted.)  
  176.      * 
  177.      * @param enumType the <tt>Class</tt> object of the enum type from which 
  178.      *      to return a constant 
  179.      * @param name the name of the constant to return 
  180.      * @return the enum constant of the specified enum type with the 
  181.      *      specified name 
  182.      * @throws IllegalArgumentException if the specified enum type has 
  183.      *         no constant with the specified name, or the specified 
  184.      *         class object does not represent an enum type 
  185.      * @throws NullPointerException if <tt>enumType</tt> or <tt>name</tt> 
  186.      *         is null 
  187.      * @since 1.5 
  188.      */  
  189.     public static <T extends Enum<T>> T valueOf(Class<T> enumType,  
  190.                                                 String name) {  
  191.         T result = enumType.enumConstantDirectory().get(name);  
  192.         if (result != null)  
  193.             return result;  
  194.         if (name == null)  
  195.             throw new NullPointerException("Name is null");  
  196.         throw new IllegalArgumentException(  
  197.             "No enum const " + enumType +"." + name);  
  198.     }  
  199.   
  200.     /** 
  201.       * prevent default deserialization 
  202.       */  
  203.     private void readObject(ObjectInputStream in) throws IOException,  
  204.         ClassNotFoundException {  
  205.             throw new InvalidObjectException("can't deserialize enum");  
  206.     }  
  207.   
  208.     private void readObjectNoData() throws ObjectStreamException {  
  209.         throw new InvalidObjectException("can't deserialize enum");  
  210.     }  
  211.   
  212.     /** 
  213.      * enum classes cannot have finalize methods. 
  214.      */  
  215.     protected final void finalize() { }  
  216. }  


//-------------------------------------------------------------------------------------------------------------------------------------------------------------

enum Color {
	RED("红", 0), BLUE("蓝", 1), GREEN("绿", 2);
	
	Color(String arg, int index) {
		System.out.println("Test Enum");
	}
}


public class Test {
	public static void main(String[] args) {
		Color color = Color.BLUE;
	}
}

输出:

Test Enum
Test Enum
Test Enum

使用jd-gui查看.class文件发现,编译器给默认的Color(String arg, int index)自动加上了private修饰符了。



使用javap -c 查看.class源文件,发现这几个RED,BLUE,GREEN枚举值,其实就是Color类型的,public static fianl Color类型的变量。



可以看出枚举类型,本质上继承java.lang.Enum类型。所以可以有构造函数。

还要注意的就是这个Color这个class是final class类型。

可以看到为什么输出了三个Test Enum了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值