Java列挙タイプenum

10432 ワード

JDKバージョンは次のとおりです:1.8.0_121
列挙タイプの導入
列挙タイプはJava 5に追加された小さな特性であり、それ以前に列挙を実現する方法はコンパイル期間定数形式を定義する.コードにコンパイル期間定数を用いる方式は理解性と安全性の面で不足しており,単純な定数数値では列挙例としての目的を直感的に体現することができず,実際には無限の取値空間も理論的に限られた列挙空間とうまくマッチングできない.
一般的な例証は四季のコードの中で表現します
定数形式は次のように表されます.
class Season{
    
    public static final int SPRING = 1;

    public static final int SUMMER = 2;

    public static final int AUTUMN = 3;

    public static final int WINTER = 4;
    
}

Enum列挙タイプは、次のように表されます.
enum Season{
    SPRING, SUMMER, AUTUMN, WINTER;
}

タイプの定義から両者に明らかな違いが見られ,列挙タイプのインスタンスは数値の制限から脱し,その役割と目的をインスタンス自身で表現できる.
列挙タイプの使用
Enumキーワードは、クラス構造の定義を完了するためによく使用されるclassキーワードと同様の役割を果たすため、enumは特殊なクラス定義として理解することができる.定義プロセスはclassの定義クラスプロセスと基本的に同じであり、enum定義のクラスはデフォルトでEnumクラスを継承している点が異なり、Javaではマルチ継承が許可されていないため、enum定義を使用するクラスは他のクラスを継承できない.
使用例:
enum Season {
    SPRING("first season"), SUMMER("second season"), AUTUMN("third season"), WINTER("last season");

    private String describe;

    Season(String describe) {
        this.describe = describe;
    }

    public String toString() {
        return describe;
    }
}

class Test {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
    }
}

出力:
first season

TOStringメソッドが再定義されているため、印刷内容はインスタンスを列挙した記述内容である.
  • インスタンス属性name、ordinal、および同名メソッド
  • この例では、Season列挙タイプにdescribe属性を追加します.Enumクラスには、列挙インスタンス名とシーケンス番号を表す2つの属性nameとordinalがあり、インスタンス属性値を返すための同名関数が提供されています.
    name、ordinalプロパティの印刷例:
    class Test {
        public static void main(String[] args) {
            Season spring = Season.SPRING;
            System.out.println(spring);
            System.out.println(spring.name());
            System.out.println(spring.ordinal());
        }
    }
    

    出力:
    first season
    SPRING
    0
    

    Enumソースの2つの属性と同名の関数の定義を確認します.
    public abstract class Enum>
            implements Comparable, Serializable {
    
        private final String name;
    
        public final String name() {
            return name;
        }
    
        private final int ordinal;
    
        public final int ordinal() {
            return ordinal;
        }
    
        protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
        ...  // omit some content
    }
    

    ソースコードからEnumのコンストラクション関数はprotectedレベルであることがわかり、例のSeason列挙クラス内部宣言の4つのインスタンスを観察すると、ベースクラスのコンストラクション関数は明示的に呼び出されず、自身のコンストラクション関数も明示的に呼び出されていないことが分かった.このことから、コンパイラがコンストラクタを呼び出して処理するクラス内部で定義された複数のインスタンスを列挙することが分かる.
    Seasonクラス定義ファイルの逆コンパイル:
    final class Season extends Enum
    {
    
        public static Season[] values()
        {
            return (Season[])$VALUES.clone();
        }
    
        public static Season valueOf(String s)
        {
            return (Season)Enum.valueOf(t1/Season, s);
        }
    
        private Season(String s, int i, String s1)
        {
            super(s, i);
            describe = s1;
        }
    
        public String toString()
        {
            return describe;
        }
    
        public static final Season SPRING;
        public static final Season SUMMER;
        public static final Season AUTUMN;
        public static final Season WINTER;
        private String describe;
        private static final Season $VALUES[];
    
        static 
        {
            SPRING = new Season("SPRING", 0, "first season");
            SUMMER = new Season("SUMMER", 1, "second season");
            AUTUMN = new Season("AUTUMN", 2, "third season");
            WINTER = new Season("WINTER", 3, "last season");
            $VALUES = (new Season[] {
                SPRING, SUMMER, AUTUMN, WINTER
            });
        }
    }
    

    逆コンパイルの内容から分かるように、Season内部で宣言された複数のインスタンスは、コンパイラが静的コードライブラリでSeason自身とベースクラスのコンストラクション関数を呼び出して定義を完了する.
  • valueOfとvalues関数
  • 上の図の逆コンパイル内容から、valueOf関数はEnumソースコードで定義されているが、EnumソースコードのvalueOf関数とここで逆コンパイルして生成されたvalueOf関数の実装を比較すると、コンパイラによって生成されたvalueOf関数の内部呼び出しは、Enumクラスで定義されたvalueOf関数であることがわかる.
        // compiler generation
        public static Season valueOf(String s) {
            return (Season)Enum.valueOf(t1 / Season, s);
        }
    
        // defined in the source code
        public static > T valueOf(Class enumType, String name) {
            T result = enumType.enumConstantDirectory().get(name);
            if (result != null)
                return result;
            if (name == null)
                throw new NullPointerException("Name is null");
            throw new IllegalArgumentException(
                "No enum constant " + enumType.getCanonicalName() + "." + name);
        }
    

    上の図の1つ目はコンパイラで生成されたvalueOf関数体で、2つ目はEnumソースコードで定義された関数体です.第2の実装を見ると、実際にはClassクラスで定義されたenumConstantDirectory関数が呼び出され、この関数内でパラメータenumTypeが列挙タイプであるか否かを判断し、列挙インスタンス名と列挙インスタンスからなるmapオブジェクトを返すことができる.
    逆コンパイルで生成されたvalues関数は、クラス内部で宣言されたインスタンス配列を列挙するcloneオブジェクトを返します.
    valueOf、values関数の例:
    class Test {
        public static void main(String[] args) {
            System.out.println(Arrays.asList(Season.values()));
            System.out.println(Season.valueOf("SUMMER"));
        }
    }
    

    出力:
    [first season, second season, third season, last season]
    second season
    

    TOString関数が書き換えられているのでdescribe属性で構成された配列として出力され、列挙インスタンスのname値に基づいてvalueOf関数はmapセットから列挙インスタンスを返します.
  • compareTo関数
  • EnumクラスはComparableインタフェースを実装しているが、Comparableインタフェースには関数宣言、すなわちcomparareTo関数が1つしかないため、列挙インスタンス間で比較することができる.比較の結果は整数値で、比較の内容はインスタンスを列挙する宣言シーケンス番号であり、比較の方法は宣言シーケンス番号の差、すなわちordinal属性の差である.
    compareTo関数の例:
    class Test {
        public static void main(String[] args) {
            Season summer = Season.valueOf("SUMMER");
            for(Season s : Season.values()){
                System.out.print(summer.name()+" compare to "+s.name());
                System.out.println(" result: "+summer.compareTo(s));
            }
        }
    }
    

    出力:
    SUMMER compare to SPRING result: 1
    SUMMER compare to SUMMER result: 0
    SUMMER compare to AUTUMN result: -1
    SUMMER compare to WINTER result: -2
    

    正の値を返すと、現在のオブジェクトが大きく、0はサイズが等しく、負の値は現在のオブジェクトが小さく、差は宣言順序の差の大きさを表します.
  • 列挙タイプのswitch用法
  • 列挙タイプは有限空間の要素集合を記述するので,要素の判断は一般的な操作であるべきである.列挙タイプはswitch用法をサポートするため,複数のif−else判定の形式を回避する.
    switchの使用例:
    class Test {
        public static void main(String[] args) {
            Season summer = Season.valueOf("SUMMER");
            switch (summer){
                case SPRING:
                    System.out.println(Season.SPRING);
                    break;
                case SUMMER:
                    System.out.println(Season.SUMMER);
                    break;
                case AUTUMN:
                    System.out.println(Season.AUTUMN);
                    break;
                case WINTER:
                    System.out.println(Season.WINTER);
                    break;
                default:
                    System.out.println("there is something wrong!");
            }
        }
    }
    

    出力:
    second season
    
  • 列挙クラスにおけるインタフェース関数
  • の実装
    列挙タイプはエンムクラスを暗黙的に継承しているため、他のクラスを継承することはできない.インタフェースを実装することで、同じインタフェースを実装した複数の列挙タイプを同じクラスとして、列挙タイプの区分と分類を実現することができる.列挙タイプのすべてのインスタンスがクラス内で定義されるため、列挙クラスがインタフェースを実装する方法は通常のクラスとは少し異なります.
    クラス内部実装インタフェース関数
    enum Season implements Rain{
        SPRING("first season"), SUMMER("second season"), AUTUMN("third season"), WINTER("last season");
    
        private String describe;
    
        Season(String describe) {
            this.describe = describe;
        }
    
        public String toString() {
            return describe;
        }
    
        public void rain(){  // implement interface method
            System.out.println(this+" is raining.");
        }
    }
    
    class Test {
        public static void main(String[] args) {
            Season summer = Season.valueOf("SUMMER");
            for(Season s : Season.values()){
                s.rain();
            }
        }
    }
    

    出力:
    first season is raining.
    second season is raining.
    third season is raining.
    last season is raining.
    

    インタフェース関数の実装は、列挙クラス内で完了し、すべての列挙タイプが同じ関数実装を呼び出します.
    インスタンス内部実装インタフェース関数
    enum Season implements Rain{
        SPRING("first season"){
            public void rain(){  // implement interface method
                System.out.println("first season is raining.");
            }
        }, SUMMER("second season"){
            public void rain(){  // implement interface method
                System.out.println("second season is raining.");
            }
        }, AUTUMN("third season"){
            public void rain(){  // implement interface method
                System.out.println("third season is raining.");
            }
        }, WINTER("last season"){
            public void rain(){  // implement interface method
                System.out.println("last season is raining.");
            }
        };
    
        private String describe;
    
        Season(String describe) {
            this.describe = describe;
        }
    
        public String toString() {
            return describe;
        }
        
    }
    
    class Test {
        public static void main(String[] args) {
            Season summer = Season.valueOf("SUMMER");
            for(Season s : Season.values()){
                s.rain();
            }
        }
    }
    

    出力:
    first season is raining.
    second season is raining.
    third season is raining.
    last season is raining.
    

    列挙クラス内部宣言は、すべての列挙インスタンスを定義するため、各インスタンスがインタフェース関数を実装する場合、クラス内部でインタフェース関数を実装しなくてもよい.クラス内部で実装されても、各インスタンスの構造時にインタフェース関数が書き換えられるからである.したがって、インタフェース関数が各インスタンスに実装されていない場合、列挙クラス内で実装されなければならないことが分かる.
    一部のインスタンスの内部実装インタフェース関数
    enum Season implements Rain {
        SPRING("first season") ,
        SUMMER("second season") {
            public void rain() {  // implement interface method
                System.out.println("heavy rain.");
            }
        }, AUTUMN("third season"), WINTER("last season");
    
        private String describe;
    
        Season(String describe) {
            this.describe = describe;
        }
    
        public String toString() {
            return describe;
        }
    
        public void rain() {  // implement interface method
            System.out.println("patter raining.");
        }
    }
    

    同じクラスに分類される列挙タイプでは、あるインスタンスが他のインスタンスと異なる場合、単独で実装できます.
    参照:
    Java Enums Tutorial A Guide to Java Enums