Java:枚举(枚举类型,核心机制,类集枚举,接口方法)

258 阅读8分钟

对于大部分面向对象编程来说,类和接口是完全够用的,但是一些特殊情况下,这些方法就不适合。例如:想定义一个 Color 类,它只能有 Red,Green,Blue 3个值,其他的任何值都是非法的,如果采用传统的构造是极其麻烦的,另外还有一些不安全的问题。引入枚举(Enum)就能避免这些问题。对于大部分面向对象编程来说,类和接口是完全够用的,但是一些特殊情况下,这些方法就不适合。

例如:想定义一个 Color 类,它只能有 Red,Green,Blue 3个值,其他的任何值都是非法的,如果采用传统的构造是极其麻烦的,另外还有一些不安全的问题。引入枚举(Enum)就能避免这些问题。

1,使用简单程序完成枚举的功能

使用类完成枚举的功能:

    package Test;

    public class Color {
       public static final Color RED = new Color("红色");
       public static final Color GREEN = new Color("绿色");
       public static final Color BLUE = new Color("蓝色");
       private String name;

       public Color(String name) {
           this.name = name;
       }
    }

将 Color 类中的构造方法私有,之后在类中准备了若干个实例化对象,以后如果要取得 Color 类的实例,则只能在RED、GREEN、BLUE 3个对象,这样有效限制了对象的取值范围。

使用接口完成枚举的功能

package Test;

public interface Color {
    public static final int RED=1;
    public static final int GREEN=2;
    public static final int BLUE=3;
}

使用上述方法定义,存在如下问题:

  • 类型不安全:因为上面的每个颜色实际上是一个 int 型,两个颜色常量相加的结果,会让人感到迷惑,操作不明确。
  • 没有命名空间:可能存在其他类中的静态常量与之混淆。
  • 打印意义不明:当输出某个颜色时,实际上输出的确实数字。

2,定义一个枚举类型

Java 5 新增了一个 enum 关键字(与 class、interface 关键字地位相同),用以定义枚举类。枚举类是一个特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。一个 Java 源文件中最多只能定义一个 public 访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。

package Test;

public enum Color {
    RED,GREEN,BLUE;
}

(1)枚举值可以直接输出。

for (Color c:Color.values()){
    System.out.println(c);
}

(2)枚举类型的数据也可以使用 枚举.values() 的形式,将全部的枚举类型变为对象数组的形式,之后使用 foreach 进行输出。

(3)枚举中的每个类型可以使用 switch 进行判断。

for (Color c:Color.values()){
    switch (c){
        case BLUE:System.out.println("这里是蓝色");break;
        case RED:System.out.println("这里是红色");break;
        case GREEN:System.out.println("这里是绿色");break;
     }
}

枚举类与普通类的区别:

  • 枚举类可以实现一个或多个接口,使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,而不是默认继承 Object 类,因此枚举类不能显式继承其他父类。其中java.lang.Enum实现了 java.lang.Searializablejava.lang.Comparable 两个接口。
  • 使用enum定义、非抽象的枚举类默认会使用final修饰。
  • 枚举类的构造器只能使用private访问修饰符,如果省略,依旧是 private 修饰。由于子类构造器总要调用父类构造器一次,因此枚举类不能派生子类。
  • 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例,系统会自动添加 public static final 修饰,无须显式添加。

3,深入理解枚举(Enum)

java.lang.Enum 类的定义如下:

public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>,java.io.Serializable

从 Enum 类的定义可以看出来,此类实现了ComparableSerializable 两个接口,证明枚举类型可以使用比较器或进行序列化操作。

枚举类的主要操作方法:

方法类型描述
protected Enum(String name,int ordinal)构造接收枚举的名称和枚举的常量创建枚举对象
protected final Object clone() throws CloneNotSupportException普通克隆枚举对象
public final int compareTo(E o)普通对象的比较
public final boolean equals(Object other)普通比较两个枚举对象
public final int hashCode()普通返回枚举常量的哈希码
public final String name()普通返回枚举的名称
public final int ordinal()普通返回枚举常量的序数
public static <T extends Enum> T valueOf (Class enumType,String name)普通返回带指定名称的指定枚举类型的枚举常量

取得枚举的信息:在枚举建立完成之后,实际上都会调用 Enum 类中的构造方法,为其赋值。在 Enum 类的构造方法中的第一个参数 name 就是定义的枚举的名称,第二个参数 ordinal 则会从 0 开发依次进行编号。之后可以使用 Enum 类中提供的 name() 和 ordinal() 方法取得名称和编号。

为每一个枚举对象属性赋值:

  • 通过构造方法为属性赋值:每个枚举类中都有其指定好的若干对象,每一个枚举值对象中也可以包含多个属性。而这些属性也可以通过构造方法为其赋值。
public enum Color {
    RED("红色"),GREEN("绿色"),BLUE("蓝色");
    private Color(String name){
        this.setName(name);
    }
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
---------------------------------
for(Color c:Color.values()){
    System.out.println(c.ordinal()+"---->"+c.name()+"---->"+c.getName());
}
=================================
0---->RED---->红色
1---->GREEN---->绿色
2---->BLUE---->蓝色    
  • 通过 setter 方法为属性赋值:通过调用 setter() 的方式为指定的属性进行赋值。但是这样一来就必须明确每一个枚举类的对象,如果操作的对象是 RED,则名字应该是“红色”;如果操作的对象是 GREEN,则名字应该是“绿色”等。
public enum Color {
    RED,GREEN,BLUE;
    private String name;
    public void setName(String name) {
        switch (this){
            case GREEN:{
                if ("绿色".equals(name)){
                    this.name = name;
                }else{
                    System.out.println("设置内容出错了!");
                }
                break;
            }
            case RED:{
                if ("红色".equals(name)){
                    this.name = name;
                }else{
                    System.out.println("设置内容出错了!");
                }
                break;
            }
            case BLUE:{
                if ("蓝色".equals(name)){
                    this.name = name;
                }else{
                    System.out.println("设置内容出错了!");
                }
                break;
            }
        }
    }
    public String getName() {
        return name;
    }
}
public static void main(String[] args) throws BeansException  {
        Color c = Color.BLUE;
        c.setName("兰色");
        c.setName("蓝色");
        System.out.println(c.getName());
}
=========================================
设置内容出错了!
蓝色
  • 直接调用:如果不想使用通过 枚举类.valueOf() 的形式取得每一个枚举类的对象,可以使用 Enum 类定义的 枚举类.valueOf() 方法显式调用。
Color c = Enum.valueOf(Color.class,"BLUE");

使用比较器 :在 Enum 类的定义中已经实现好了 Comparable 接口,所以枚举类的内容本身是可以进行排序的。

enum Color{
    RED,GREEN,BLUE;
}
public class Test {
    public static void main(String[] args) throws BeansException  {
        Set<Color> t = new TreeSet<Color>();
        t.add(Color.GREEN);
        t.add(Color.BLUE);
        t.add(Color.RED);
        Iterator<Color> iterator = t.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next()+"---");
        }
    }
}
==================================
RED---
GREEN---
BLUE---

加入的顺序与输出顺序不同,证明已经排序过了,是使用 Enum 类中的 ordinal 属性排序的。

4,类集对枚举的支持

JDK1.5 的 java.util 程序包中提供了两个新的集合操作类:EnumMap 和 EnumSet,这两个类与枚举类型的结合应用可使以前非常烦琐的程序变得简单方便。

EnumMap:EnumMap 是 Map 接口的子类,所以本身还是以 Map 的形式进行操作,即 key——value。如果要使用 EnumMap,则首先应该常见 EnumMap 的对象,在创建此对象时必须指定要操作的枚举类型。

enum Color{
    RED,GREEN,BLUE;
}
public class Test {
    public static void main(String[] args) throws BeansException  {
        Map<Color,String> desc = new EnumMap<Color, String>(Color.class);
        desc.put(Color.RED,"红色");
        desc.put(Color.GREEN,"绿色");
        desc.put(Color.BLUE,"蓝色");
        for (Color c:Color.values()){
            System.out.println(c.name()+"--->"+desc.get(c));
        }
    }
}
===========================================
RED--->红色
GREEN--->绿色
BLUE--->蓝色

EnumSet:EnumSet 是 Set 接口的子类,所以里面的内容是无法重复的。使用 EnumSet 时不能直接使用关键词 new 为其实例化,而是使用本类中提供的静态方法。

方法描述
public static<E extends Enum> EnumSet allOf(Class elementType)将枚举中的全部内容设置到EnumSet中
public static<E extends Enum> EnumSet of(E first,E...rest)创建一个包含枚举指定内容的EnumSet对象
public static<E extends Enum> EnumSet copyOf(Collection e)创建一个从指定Collection中指定的EnumSet对象
public static<E exends Enum> EnumSet complementOf(EnumSet s)创建一个其元素类型与指定枚举set相同的枚举set,最初包含指定集合中所包含的此类型的所有元素
public static<E extends Enum> EnumSet noneOf(Class elementType)创建一个可以接收指定类的空集合

(1)将全部的集合设置到 EnumSet 集合中

EnumSet<Color> es = EnumSet.allOf(Color.class);
for (Color c:es){
    System.out.print(c+"、");
}
=========================
RED、GREEN、BLUE、

(2)只设置一个枚举的类型到集合中

EnumSet<Color> es = EnumSet.of(Color.BLUE);
for (Color c:es){
    System.out.println(c+"、");
}
================================
BLUE、   

(3)创建只能放入指定枚举类型的集合

EnumSet<Color> es = EnumSet.noneOf(Color.class);
es.add(Color.BLUE);
es.add(Color.GREEN);
for (Color c:es){
    System.out.println(c+"、");
}

(4)创建不包含指定元素的集合

EnumSet<Color> esOld = EnumSet.noneOf(Color.class);
esOld.add(Color.BLUE);
esOld.add(Color.GREEN);
EnumSet<Color> esNew = EnumSet.complementOf(esOld);
for (Color c:esNew){
    System.out.println(c+"、");
}
========================
RED、 

(5)复制已有的内容

EnumSet<Color> esOld = EnumSet.noneOf(Color.class);
esOld.add(Color.BLUE);
esOld.add(Color.GREEN);
EnumSet<Color> esNew = EnumSet.copyOf(esOld);
for (Color c:esNew){
    System.out.println(c+"、");
}

5,让枚举类实现一个接口

枚举类也可以实现一个接口,但是因为接口中会存在抽象方法,所以枚举类中的每个对象都必须分别实现接口中的抽象方法。

interface Print(){
    public String  getColor();
}
enum Color implements Print{
    RED,GREEN,BLUE;

    public String getColor() {
        RED{
            public String getColor(){
                return "红色";
            }
        },
        BLUE{
            public String getColor(){
                return "蓝色";
            }
        },
        GREEN{
            public String getColor(){
                return "绿色";
            }
        };
    }
}

6,在枚举中定义抽象方法

enum Color implements Print {
    RED {
        public String getColor() {
            return "红色";
        }
    },
    BLUE {
        public String getColor() {
            return "蓝色";
        }
    },
    GREEN {
        public String getColor() {
            return "绿色";
        }
    };
    public abstract String getColor();
}