zoukankan      html  css  js  c++  java
  • 枚举(enum)

    枚举类型是指由一组固定的常量组成的合法值的类型。例如一年中的季节,太阳系的行星或者一副牌的花色等,在还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具名的int常量,一个类型成员一个常量,如下所示:

    1     public static final int APPLE_FUJI = 0;
    2     public static final int APPLE_PIPPIN = 1;
    3     
    4     public static final int ORANGE_NAVEL = 0;
    5     public static final int ORANGE_TEMPLE = 1;

    这样的方式叫做int枚举模式,这种模式有很多缺点:

      ⑴ 在类型的安全性和使用的方便性来说,对coder来说没有任何帮助;

      ⑵ 可以将不同代表性的值互相引用,并且编译器也不会出现警告(你可以将apple传到orange的方法中),这样即使程序不会出错,但对于代码的阅读者来说,很有可能会弄错;

      ⑶ 采用这种模式的程序十分脆弱,因为int枚举是编译时常量,被编译到使用它们的类中,如果与枚举常量关联的int发生了变化,那么这个类就必须重新编译,如果不重新编译,程序还是可以运行,但是运行的结果就不确定了(静态final常量在类编译的时候就确定了);

      ⑷ 遍历或者获得int枚举组的大小,没有很可靠的方法,调试或者打印出来的信息没有太大的用处,同时也很不方便;

     枚举(enum)

      可以替代这种模式,在避免了int枚举模式的缺点,同时提供了许多额外的好处。

      从Java1.5开始,我们就可以使用枚举来代替上述的模式了,下面简单将上述例子修改为枚举类型:

        public enum Apple{FUJI,PIPPIN}
        public enum Orange{NAVEL,TEMPLE}

       Java枚举类型背后的基本想法非常简单:它们就是通过公有的静态final域为每个枚举常量导出实例的类。对Java来说,Java的枚举本质上是int值。

    枚举具有很多特点:

      ⑴ 因为没有可以访问的构造器,枚举类型是真正的final。因为使用枚举的类既不能创建枚举的实例,也不能对其进行扩展,因此很有可能没有实例,而只有声明过的枚举常量。换句话说就是,枚举类型是实例受控的。它们是单例的泛型化,本质上是单元素的枚举;

      ⑵ 枚举提供了编译时的类型安全;如果声明一个参数的类型为Apple,就可以保证被传到该参数上的任何非null的对象引用一定属于两个有效的Apple值之一。试图传递类型错误的值时,会导致编译时错误,就像试图将某种枚举类型的表达式赋给另一种枚举常量的变量,或者试图利用==来比较不同枚举类型一样(避免了上述int枚举模式的第二种缺点)。

      ⑶ 包含同名常量的多个枚举类型可以在一个系统中和平共处,因为每个类型都有自己的命名空间(你可以在Apple里面定义一个WEIGHT,同时在Orange里面定义一个WEIGHT)。

      ⑷ enum的常量值并没有被编译到使用它们的类中,而是在int枚举模式中。这样在枚举类型和使用它们的类中提供了一个隔离层。你可以增加或者重新排列枚举类型的常量,无需重新编译使用它们的类的代码。而且,你还可以通过toString方法,将枚举类型转换成打印的字符串。

      ⑸ 枚举类型允许添加任意的方法和域,并且还可以实现任意的接口。提供了所有的Object方法的高级实现,实现了Comparable和Serializable接口,并且还对枚举类型的可任意改变性设计了序列化的方式。

      ⑹ 枚举与int常量相比,枚举有个小小的性能缺点,即在装载和初始化枚举时会有空间和时间的成本(除了资源受限的设备,例如手机和烤面包机外,在实际中不必太在意这个问题)。

    前面4个特点比较简单,说说第五个特点,看个例子:

     1 public enum Planet {
     2     MERCURY(3.302e+23,2.439e6),
     3     VENUS(4.869e+24,6.052e6),
     4     EARTH(5.975e+24,6.378e6),
     5     MARS(6.419e+23,3.393e6),
     6     JUPITER(1.889e+27,7.149e7);
     7 
     8     private final double mass;
     9     private final double radius;
    10     private final double surfaceGravity;
    11 
    12     Planet(double mass,double radius){
    13         this.mass = mass;
    14         this.radius = radius;
    15         this.surfaceGravity = 10 * mass / (radius * radius);
    16     }
    17 
    18     public double mass(){
    19         return mass;
    20     }
    21 
    22     public double radius(){
    23         return radius;
    24     }
    25 
    26     public double surfaceGravity(){
    27         return surfaceGravity;
    28     }
    29 
    30     public double surfaceWeight(double mass){
    31         return mass * surfaceGravity;
    32     }
    33 }

       编写一个像这样的Plant枚举类并不难。为了将数据与枚举常量联系起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。枚举天生就是不可变的,因此所有的域都应该是final的,这些域可以是公有的,但最好是私有的,并提供公有的访问方法。虽然这个枚举很简单,但是功能却很强大,如下所示:

    1 public static void main(String[] args){
    2         double earthWeight = 175d;
    3         double mass = earthWeight / Planet.EARTH.surfaceGravity();
    4         for (Planet p : Planet.values()){
    5             System.out.printf("Weight on %s is %f%n ",p,p.surfaceWeight(mass));
    6         }
    7     }

    输出结果:

    1  Weight on MERCURY is 66.133672
    2  Weight on VENUS is 158.383926
    3  Weight on EARTH is 175.000000
    4  Weight on MARS is 66.430699
    5  Weight on JUPITER is 440.362707

      可以看出通过一段简单的代码就可以实现很多的功能,并且对打印出来的信息一目了然。

    EnumSet和EnumMap

    enumSet和enumMap分别是枚举类型的set和map,看一下它们的用法:

     1 public static void main(String[] args){
     2         EnumSet<Planet> es = EnumSet.allOf(Planet.class);
     3         for (Planet ed : es)
     4             System.out.println(ed.name() + ":" + ed.ordinal());
     5 
     6         System.out.println("
    -----EnumSet和EnumMap之间的分隔线-----
    ");
     7 
     8         EnumMap<Planet, String> em = new EnumMap<Planet, String>(Planet.class);
     9         em.put(Planet.MERCURY, "水星");
    10         em.put(Planet.VENUS, "金星");
    11         em.put(Planet.EARTH, "地球");
    12         em.put(Planet.MARS, "火星");
    13         em.put(Planet.JUPITER, "木星");
    14 
    15         Iterator<Entry<Planet, String>> iterator = em.entrySet().iterator();
    16         while (iterator.hasNext()){
    17             Entry<Planet, String> entry = iterator.next();
    18             System.out.println(entry.getKey().name() + ":" + entry.getValue());
    19         }
    20     }

    输出结果:

    MERCURY:0
    VENUS:1
    EARTH:2
    MARS:3
    JUPITER:4
    
    -----EnumSet和EnumMap之间的分隔线-----
    
    MERCURY:水星
    VENUS:金星
    EARTH:地球
    MARS:火星
    JUPITER:木星

    注意:

      enumSet和enumMap都是非线程安全的。

      总而言之,与int常量相比,枚举类型的优势是不言而喻的。枚举要以读得多,也更加安全,功能更加强大。每当需要一组固定常量的时候,就应该使用枚举(行星,一周的天数,棋子的数目等)。

    参考:《Effective Java》中文版 第二版;

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    为什么 PCB 生产时推荐出 Gerber 给工厂?
    Fedora Redhat Centos 有什么区别和关系?
    【KiCad】 如何给元件给元件的管脚加上划线?
    MCU ADC 进入 PD 模式后出现错误的值?
    FastAdmin 生产环境升级注意
    EMC EMI 自行评估记录
    如何让你的 KiCad 在缩放时不眩晕?
    KiCad 5.1.0 正式版终于发布
    一次单片机 SFR 页引发的“事故”
    java基础之集合
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/9761658.html
Copyright © 2011-2022 走看看