Java枚举功能虽小但价值可观

枚举简介

Java 5 中添加了一个看似很小的特性 enum 关键字,它使得我们在需要群组并使用枚举类型集时,可以很方便地处理。以前,你需要创建一个整数常量集,但是这些值并不会将自身限制在这个常量集的范围内,因此使用它们更有风险,而且更难使用。枚举类型属于非常普遍的需求,C、C++ 和其他许多语言都已经拥有它了。在 Java 5 之前,Java 程序员必须了解许多细节并格外仔细地去达成 enum 的效果。现在 Java 也有了 enum,并且它的功能比 C/C++ 中的完备得多。下面是个简单的例子:

package com.enums;

public enum FileTypeEnum {
    DOC, XLS, PDF;
}

这里创建了一个名为 FileTypeEnum 的枚举类型,它有3个值。由于枚举类型的实例是常量,因此按照命名惯例,它们都用大写字母表示(如果名称中含有多个单词,使用下划线分隔)。要使用 enum,需要创建一个该类型的引用,然后将其赋值给某个实例:

package com.enums;

public class FileTypeEnumCase {
    public static void main(String[] args) {
        FileTypeEnum doc = FileTypeEnum.DOC;
        System.out.println(doc);
    }
}

输出:

DOC

枚举特性

在你创建 enum 时,编译器会自动添加一些有用的特性。例如,它会创建 toString() 方法,以便你方便地显示某个 enum 实例的名称,这从上面例子中的输出可以看出。编译器还会创建 ordinal() 方法表示某个特定 enum 常量的声明顺序,static values() 方法按照 enum 常量的声明顺序,生成这些常量值构成的数组.。创建 enum 时,编译器会为你生成一个相关的类,这个类继承自 Java.lang.Enum。下面的例子演示了 Enum 提供的一些功能:

package com.enums;

public class FileTypeEnumCase {
    public static void main(String[] args) {
        for(FileTypeEnum f : FileTypeEnum.values()){
            System.out.println(f + " ordinal: " + f.ordinal());
            System.out.print(
                    f.compareTo(FileTypeEnum.DOC) + " ");
            System.out.print(
                    f.equals(FileTypeEnum.DOC) + " ");
            System.out.println(f == FileTypeEnum.DOC);
            System.out.println(f.getDeclaringClass());
            System.out.println(f.name());
            System.out.println("********************");
        }
    }
}

输出:

DOC ordinal: 0
0 true true
class com.enums.FileTypeEnum
DOC
********************
XLS ordinal: 1
1 false false
class com.enums.FileTypeEnum
XLS
********************
PDF ordinal: 2
2 false false
class com.enums.FileTypeEnum
PDF
********************

ordinal() 方法返回一个 int 值,这是每个 enum 实例在声明时的次序,从 0 开始。

可以使用==来比较 enum 实例,编译器会自动为你提供 equals() 和 hashCode() 方法。Enum 类实现了 Comparable 接口,所以它具有 compareTo() 方法。同时,它还实现了 Serializable 接口。

如果在 enum 实例上调用 getDeclaringClass() 方法,我们就能知道其所属的 enum 类。

name() 方法返回 enum 实例声明时的名字,这与使用 toString() 方法效果相同。valueOf() 是在 Enum 中定义的 static 方法,它根据给定的名字返回相应的 enum 实例,如果不存在给定名字的实例,将会抛出异常。

enum 有一个很实用的特性,就是在 switch 语句中使用:

public class FileTypeEnumCase {

    FileTypeEnum fileType;

    public FileTypeEnumCase(FileTypeEnum fileType) {
        this.fileType = fileType;
    }

    public void getFileType() {
        System.out.print("This file type is ");
        switch(fileType) {
            case DOC:
                System.out.println("doc");
                break;
            case XLS:
                System.out.println("xls");
                break;
            case PDF:
                System.out.println("pdf");
                break;
            default:
                System.out.println("This file type is mismatch!");
        }
    }
}

由于 switch 是在有限的可能值集合中选择,因此它与 enum 是绝佳的组合。注意,enum 的名称是如何能够倍加清楚地表明程序的目的的。

枚举的values 方法

前面已经提到,编译器为你创建的 enum 类都继承自 Enum 类。然而,如果你研究一下 Enum 类就会发现,它并没有 values() 方法。可我们明明已经用过该方法了,难道存在某种“隐藏的”方法吗?答案是,values() 是由编译器添加的 static 方法。编译器还为其添加了 valueOf() 方法。这可能有点令人迷惑,Enum 类不是已经有 valueOf() 方法了吗。不过 Enum 中的 valueOf() 方法需要两个参数,而这个新增的方法只需一个参数。由于这里使用的 Set 只存储方法的名字,而不考虑方法的签名,所以在调用 Explore.removeAll(Enum) 之后,就只剩下[values] 了。

由于 values() 方法是由编译器插入到 enum 定义中的 static 方法,所以,如果你将 enum 实例向上转型为 Enum,那么 values() 方法就不可访问了。不过,在 Class 中有一个 getEnumConstants0 方法,所以即便 Enum 接口中没有 values0 方法,我们仍然可以通过 Class 对象取得所有 enum 实例。

实现而非继承

我们已经知道,所有的 enum 都继承自 Java.lang.Enum 类。由于 Java 不支持多重继承,所以你的 enum 不能再继承其他类。然而,在我们创建一个新的 enum 时,可以同时实现一个或多个接口。在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。举例来说,假设你想用 enum 来表示不同类别的食物,同时还希望每个 enum 元素仍然保持 FileType类型。那可以这样实现:

package com.enums;

public interface FileType {
    enum Image implements FileType {
        JPG, PNG, GIF;
    }

    enum Office implements FileType {
        DOC, XLS, PPT;
    }
}

对于 enum 而言,实现接口是使其子类化的唯一办法,所以嵌入在 FileType中的每个 enum 都实现了 FileType接口。那么我们就可以将其实例向上转型为 Food,所以上例中的所有东西都是 FileType。

常量特定方法

Java 的 enum 有一个非常有趣的特性,即它允许程序员为 enum 实例编写方法,从而为每个 enum 实例赋予各自不同的行为。要实现常量相关的方法,你需要为 enum 定义一个或多个 abstract 方法,然后为每个 enum 实例实现该抽象方法。参考下面的例子:

package com.enums;

import java.text.DateFormat;
import java.util.Date;

public enum BaseEnum {
    DATE_TIME {
        @Override
        String getInfo() {
            return
                    DateFormat.getDateInstance()
                            .format(new Date());
        }
    },
    VERSION {
        @Override
        String getInfo() {
            return System.getProperty("java.version");
        }
    };
    abstract String getInfo();
    public static void main(String[] args) {
        for(BaseEnum b : values())
            System.out.println(b.getInfo());
    }
}

输出:

2021-8-4
1.8.0_102

通过相应的 enum 实例,我们可以调用其上的方法。在面向对象的程序设计中,不同的行为与不同的类关联。而通过常量相关的方法,每个 enum 实例可以具备自己独特的行为,这似乎说明每个 enum 实例就像一个独特的类。在上面的例子中,enum 实例似乎被当作其“超类”BaseEnum来使用,在调用 getInfo() 方法时,体现出多态的行为。然而,enum 实例与类的相似之处也仅限于此了。我们并不能真的将 enum 实例作为一个类型来使用。

总结

关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用。这是一种非常有用的功能。使用 enum 可以做很多有趣的事情。

虽然 Java 中的枚举比 C 或 C++中的 enum 更成熟,但它仍然是一个“小”功能。有时恰恰因为它,你才能够优雅而干净地解决问题。优雅与清晰很重要,正是它们区别了成功的解决方案与失败的解决方案。而失败的解决方案就是因为其他人无法理解它。

最后的最后

为初学者提供学习指南,为从业者提供参考价值。我坚信码农也具有产生洞见的能力。扫描下图二维码关注,学习和交流!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/pangpengshuai/article/details/119409811