乐趣区

关于后端: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 更成熟,但它依然是一个“小”性能。有时恰好因为它,你才可能优雅而洁净地解决问题。优雅与清晰很重要,正是它们区别了胜利的解决方案与失败的解决方案。而失败的解决方案就是因为其他人无奈了解它。

最初的最初

为初学者提供学习指南,为从业者提供参考价值。我深信码农也具备产生洞见的能力。扫描下图二维码关注,学习和交换!

退出移动版