枚举

来源:互联网 发布:java数据库连接代码 编辑:程序博客网 时间:2024/06/07 07:20
jdk1.5引入的用于常量定义,之前的版本只能使用类常量或者接口常量。

emun Season{
     Spring,Summer,Autumn,Winter
}

它的优势在于:
  • 1简单性
首先,我们比较下接口常量和枚举的定义,
interface Season{
     int Spring = 0;
     int Summer = 1;
     int Autumn = 2;
     int  Winter = 3;
}

定义的变量和顺序和枚举一样,枚举只需要定义枚举项,不需要定义枚举值,而接口常量需要同时定义变量和值,否则编译不通过。引用的方式都是类名.常量名,但接口常量还要关注对应的int值。

  • 2枚举常量属于稳态型

接口常量需要做越界校验。如果边界过多,一来判断条件复杂,二则可能存在编译通过,但运行产生非预期的结果。
而枚举类型,不需要越界校验,同时取值已经固定在定义的枚举项范围之内,如果取值越界,则直接编译不通过。

  • 3枚举具有内聚方法
  如果要列出接口常量类中所有的属性值,一般可以通过反射获取,但枚举可以通过内聚的方法values()获取枚举项,直接打印出来。
public static void main(String []args){
     for (Season s :Season.values()){
          System.out.println(s);
     }
}

每个枚举都是java.lang.Emun的子类,该基类定义了多种方法。

  • 4枚举可以自定义方法
枚举不仅能够定义静态方法,同时能够定义非静态方法.
public enum Season {

       Spring, Summer , Autumn, Winter;

       public static Season getComfortableSeasion () {
             return Spring ;
      }
}

public class EmunTest {

       public static void main(String[] args) {
            System. out.println("The comfortable season is "
                        + Season. getComfortableSeasion());

      }
}
每个枚举项都是该枚举的一个实例,Spring是Season的一个实例,Summer也是其中一个实例,定义的静态方法既可以在类(也就是Season)中引用,也可以在实例(也就是枚举项)中引用。
public class EmunTest {

       public static void main(String[] args) {
            System. out .println("The comfortable season is "
                        + Season. getComfortableSeasion());
             System. out .println("The comfortable season is "
                        + Season. Autumn. getComfortableSeasion());

      }
}
     
输出:
The comfortable season is Spring
The comfortable season is Spring
而在类常量中,代码如下:
如果我们此时要打印出“The comfortable season is Spring”,则只能通过switch遍历获取。

 
枚举的一大缺点在于不能继承,除非重构。但一般在项目中,定义常量的类很少需要扩展。


  • 5使用构造函数增强枚举的描述
public enum Season {

     Spring("春" ), Summer( "夏"), Autumn ("秋" ), Winter( "冬");

      private String desc ;

      Season(String desc) { /* 不能定义成public */
             this.desc = desc;
      }

       /**
       * @return the desc
       */
       public String getDesc() {
             return desc ;
      }
}
 public static void main(String[] args) {
   System. out.println("Spring description is :" + Season.Spring.getDesc());
}
输出:
Spring description is :春
enum Role{

  • 6小心switch带来的枚举空值
      

       public static void doSports(Season season) {
             switch (season) {
             case Spring :
                  System. out.println("春天放风筝" );
                   break;
             case Summer :
                  System. out.println("夏天游泳" );
                   break;
             case Autumn :
                  System. out.println("秋天捉知了" );
                   break;
             case Winter :
                  System. out.println("冬天滑冰" );
                   break;
             default:
                  System. out.println("输入错误 " );
                   break;
            }
      }

 doSports( null);
没有显示"输入错误",为啥报空指针异常。
Exception in thread "main" java.lang.NullPointerException
      at com.emun.EmunTest.doSports( EmunTest.java:18)
      at com.emun.EmunTest.main( EmunTest.java:14)
我们知道switch可以包含byte,char,short,int(Jdk7可以包含String)。所以编译器判断出switch语句后的参数是枚举类型,然后就会根据枚举的排序值继续匹配,其代码等价于:
 
所以在方法的入口处,需要判断枚举是否为null.

  • 7针对枚举,在switch的defaults中增加AssertionError错误
一般项目中,可能存在switch穷举了所有可能的值,而没有加defaults子句。如果后期新增枚举项,则编译可能没问题,但运行时会报异常。
比如日志级别:
emun LogLevel{
     INFO,DEBUG,WARN,ERROR;

}

switch(LogLevel){
     case INFO:
      .................
     case DEBUG:
     ...............
     case WARN:
      .......
     case ERROR:
     .......
}

如果新增FATAL举项,则此时switch 会由于缺少fatal或者default,导致FATAL日志丢失。

    当然也可以在eclipse中,设置Java->Compiler->Errors/WSarnings中的"Enum type constant not coverted on 'switch' "为Error级别
如果不判断所有的枚举项,就不能通过编译。
 
  • 8使用valueOf前做空值判断
//valueOf 注意summer是小写s
            List<String> params = Arrays. asList("Spring", "summer");
             for(String param:params){
                  Season s = Season. valueOf(param);
                   if(s!=null ){
                        System. out.println(s);
                  } else{
                        System. out.println("无相关枚举项" );
                  }
            }
输出:
Exception in thread "main" java.lang.IllegalArgumentException : No enum const class com.emun.Season.summer
      at java.lang.Enum.valueOf( Enum.java:192)
      at com.emun.Season.valueOf( EmunTest.java:1)
      at com.emun.EmunTest.main( EmunTest.java:33)
提示没有枚举项summer,虽然异常消息很清楚指出问题所在,但真正的原因在valueOf的源码实现:

解决的方法有两种:
1 try...catch..捕获
try {
                        Season s = Season. valueOf(param);
                         if (s != null) {
                              System. out.println(s);
                        }
                  } catch (Exception e) {
                        System. out.println("无相关枚举项" );
                  }
2修改emun类,增加判断方法
     public static boolean contains(String name) {
            Season[] seasons = values();
             for (Season s : seasons) {
                   if (s.name().equals(name)) {
                         return true;
                  }
            }
             return false;
      }

  • 9用枚举实现工厂方法
一般的工厂类实现如下:

interface Car {

}

class FordCar implements Car {

};

class BuickCar implements Car {

};

class CarFactory {

       public static Car createCar(Class<? extends Car> c) {
             try {
                   return (Car) c.newInstance();
            } catch (Exception e) {
                  e.printStackTrace();
            }
             return null ;
      }
}

       public static void main(String[] args) {

               Car car = CarFactory. createCar(FordCar. class);
     }

而枚举实现可以有以下两种方式:
1 枚举非静态方法实现

class CarFactory {

       public static Car createCar(Class<? extends Car> c) {
             try {
                   return (Car) c.newInstance();
            } catch (Exception e) {
                  e.printStackTrace();
            }
             return null ;
      }
}


enum CarFactoryEnum{
       FordCar,BuickCar ;
       public Car create(){
             switch(this ){
                   case FordCar :
                         return new FordCar();
                   case BuickCar :
                         return new BuickCar();
                   default:
                         throw new AssertionError("无效参数");
            }
      }
}


Car car = CarFactoryEnum.BuickCar.create();

2 抽象方法生成


enum CarFactoryEnumNew{
       FordCar{
             public Car create(){
                   return new FordCar();
            }
      }, BuickCar{
             public Car create(){
                   return new BuickCar();
            }
      };
       public abstract Car create();
}


枚举工厂方法的优点有:
(1)避免错误的调用
普通的工厂方法,可能存在
Car car = CarFactory.create(Car.class);
虽然编译没问题,但运行会报InstatiationException异常。
(2)性能好,使用方便
 枚举计算是以int类型计算的,所以性能上较快。
 
  • 10 枚举项数量

// 普通枚举项,数量小于64
enum Const {
       A, B , C, D, E, F , G, H, I, J , K, L, M, N , O, P, Q, R , S, T, U, V , W, X , Y, Z, AA, BA , CA, DA, EA, FA , GA, HA, NA, OA , PA, QA, RA, SA , TA, UA, VA , WA, XA, YA, ZA , BC, CC, DC, EC , FC, GC, HC, IC , JC, KC, LC, MC , NC, OC , PC, QC, RC;
}

// 大枚举,数量超过64
enum LargeConst {
       A, B , C, D, E, F , G, H, I, J , K, L, M, N , O, P, Q, R , S, T, U, V , W, X , Y, Z, AA, BA , CA, DA, EA, FA , GA, HA, IA, JA , KA, LA, MA, NA , OA, PA, QA , RA, SA, TA, UA , VA, WA, XA, YA , ZA, AB, BB, CB , DB, EB, FB, GB , HB, IB , JB, KB, LB, MB ;
}
   // 创建生成包含所有枚举项的EnumSet
   EnumSet<Const> cs = EnumSet. allOf(Const.class);
   EnumSet<LargeConst> lcs = EnumSet. allOf(LargeConst.class);
   // 打印出枚举项数量
   System. out.println("Const枚举项数量:" + cs.size());
   System. out.println("LargeConst枚举项数量:" + lcs.size());
   // 输出两个EnumSet的class
   System. out.println(cs.getClass());
   System. out.println(lcs.getClass());

Const枚举项数量:64
LargeConst枚举项数量:65
class java.util.RegularEnumSet
class java.util.JumboEnumSet

          result.addAll();
          return result; 
}



RegularEnumSet每位表示一个枚举项。
JumboEnumSet用数组保存枚举项,每个数组的元素表示64个枚举项。