Java列挙の使用と理解について.

25484 ワード

前言
列挙を紹介する前に、「魔法の数字」というもう一つの名詞について話します.魔法数字とは、コードに直接現れる数値です.例えば、user.setStatus(1);の数値1が「魔法の数字」であり、1が表す意味を直感的に理解するのは難しい.少なくとも一目で、1が何を表しているのか疑問になります.それを避けるために、開発時にコメントを追加すると思います.
注釈はいいことですが、コード自体がうまく説明できれば、この注釈を書かないのはきっと喜んでいると思います.それはあなたに心理的な負担を与えないからです.
もう一つの考えはいいと思います.コードを抽象的にすることができるとき--〔コードはすでに簡潔になっています〕メソッド名/変数名を定義するときによく自己説明できるとき--〔このときほとんどのコードは、あなたはもう一目でわかります〕最後に、適切な注釈を加えることができます.このコードは、きっと芸術感があると思います.
PS:コードを書くときは、変数ごとのネーミングをよく考え、慎重にしてください.
私の理解では、列挙の誕生は上記の問題をよりよく解決し、コードの自己説明を高めるためである.
また、コンパイル中に問題をチェックしたり、データの検証(メソッドパラメータ定義の使用)をしたりすることも重要です.これらは文章の後で一つ一つ言及されます.
本題にもどる
JAVA列挙(enum)、JDK 1.5の後に導入され、java.langパッケージ内に存在する.class、interfaceと同様の地位で列挙クラスを定義する.実際に列挙クラスは特殊なクラスであり、独自の方法、属性、コンストラクタなどを定義し、インタフェースを実現することもできる.
apiドキュメント、java.lang.Enumの説明(大まか):
//  Java            。(  Enum    )
public abstract class Enum<E extends Enum<E>>extends Object implements Comparable<E>, Serializable{
    ......
}

PS:文章の最後に完全なjavaを付録しました.lang.Enum、興味のある方はご覧ください
まず簡単なenum(以下)を定義します.
public enum Color{
    RED,BLUE,BLACK,YELLOW
}

ps:文法だから、なぜそう書いたのか追及しないでください.これは次のようなものです.
public enum Color{
    RED(),BLUE(),BLACK(),YELLOW();
}

まとめ:1.enumキーワードを使用して列挙クラスを定義します.2.表示される列挙値(最初の行でこのようにして大文字で書いてください)は、カンマで区切られています.
本質:Color列挙クラスバイトコード(大まか):
//      :1.     final;2.RED   ,    ;
public final class enums.Color extends java.lang.Enum<enums.Color> {
    public static final enums.Color RED;
    public static final enums.Color BLUE;
    public static final enums.Color BLACK;
    public static final enums.Color YELLOW;
    .........
}

列挙のいくつかの本質を得ることができます.
(1)enumで定義された列挙クラスは本質的にclassクラスである(私はやはり〔一部特殊〕で形容したいが)、javaをデフォルトで直接継承している.lang.Enumクラス.〔classと見なしてください)
(2)列挙値(RED,BLUE.....)は本質的に列挙Colorオブジェクトであり、final修飾の静的定数である.これらの値は一般的にコンストラクタを用いて初期化される(後述).
(3)列挙クラスのコンストラクタはprivateアクセス制御子のみを使用でき,非表示のコンストラクタでもprivateアクセス制御子である.PS:この点もよく理解できます.コンストラクタを使用する必要がある場合.この二つの状況にほかならない.インスタンスを作成する必要があります.しかし、列挙内の列挙値は静的であり、インスタンスを作成することによってそれを得る必要はありません.2.属性を初期化する必要がある.あなたが列挙すれば、きっとそんなことはしないと思います.それは定数でfinalだからです.
(4)enumを使用して非抽象的な列挙クラスを定義すると、デフォルトではfinal修飾が使用されます.ただし(例):列挙内に抽象メソッドがある場合、その列挙クラスは非抽象列挙クラスであり、finalによって修飾されないため、継承可能であることを意味します.(後で説明しますが、理解できない場合はスキップしてください).
実際に使用:
1.列挙クラスのインスタンスを使用する必要がある場合は、Colorなどの「列挙クラス.インスタンス」の形式を使用します.RED.ここではインスタンスを取得するだけです.実際の使用では、インスタンスの取得は最初のステップにすぎず、その後に値を取るプロセスがあります(後述します).
public class Test {
    public static void querColor(Color c) {
        switch (c) {
        case RED:
            System.out.println("     ");
            break;
        case BLUE:
            System.out.println("     ");
            break;
        case BLACK:
            System.out.println("     ");
            break;
        case YELLOW:
            System.out.println("     ");
            break;
        }
    }
    public static void main(String[] args) {
        queryColor(Color.RED); //     
    }
}

JDK1.5列挙を増やしてswitchも拡張し,switchの条件式は列挙タイプを用いることができる.switch条件式が列挙タイプを使用する場合、caseの値は列挙値名を直接使用できます.
2.列挙を使用する場合、列挙オブジェクト(列挙値)が可変であることを望むことが多い.すなわち、列挙クラスのすべてのFieldはfinalで修飾されるべきである(それ自体もそうである).ここでは一般的にコンストラクタを使用してこれらの属性に値を初期化する.(またはFieldの定義時にデフォルト値を指定したり、初期化ブロックで初期値を指定したりしますが、どちらの場合も推奨されません).実際の開発では、ビジネスニーズに応じて列挙クラスを定義する際にColorよりもデータ構造が複雑になります.たとえば、UserTypeEnum
public enum UserTypeEnum {
    ADMINISTRATOR(0, "   "), SHOP(2, "  "), WAITER(1, "  "), SELLER(3, "  ");  

    private Integer code; 
    private String  desc;

    private UserTypeEnum(Integer code, String desc){
        this.setCode(code);
        this.setDesc(desc);
    }

    public String getDesc() {
        return desc;
    }
    public Integer getCode() {
        return code;
    }
    //**     set  。**
}

上記のプログラムではADMINISTRATOR(0,管理者)、SHOP(2,店舗)、WAITER(1,「小二」)、SELLER(3,「販売」)と、以下のコードに等しい.
//      
private static final UserTypeEnum ADMINISTRATOR=new UserTypeEnum(0, "   ");
private static final UserTypeEnum SHOP=new UserTypeEnum(2, "  "),;
private static final UserTypeEnum WAITER=new UserTypeEnum(1, "  ");
private static final UserTypeEnum SELLER=new UserTypeEnum(3, "  ");

ここまで来たらよく理解する必要があります.new UserTypeEnum(2, " ") ...に等価である以上、なぜ表示を書く必要があるのか、パラメトリックコンストラクタも理解できると思います.
PS:列挙値括弧内に値がない場合(無パラメトリック構造を呼び出すと理解される)は省略できます.これも文章の冒頭をよく説明しています.
ADMINISTRATOR       ADMINISTRATOR()

値を取るプロセス:
UserTypeEnum.SHOP.getDesc()  //    "  "
UserTypeEnum.SHOP.getCode()  //    2

この時、彼は本当に「普通」のclassであることに気づきます.簡単じゃないか!
3.列挙クラスは、1つまたは複数のインタフェースを実装することもできる.通常のクラスが1つ以上のインタフェースを実装するのと同じように、クラスが1つ以上のインタフェースを実装する場合も、そのインタフェースに含まれる方法を実装する必要があります.
public interface PrintColor {
    void print();
}

public enum Color implements PrintColor{
    RED, BLUE, BLACK, YELLOW;
    @Override
    public void print() {
        System.out.println("print......");
    }

}

インタフェース内のメソッドを列挙クラスによって実装する場合、各列挙クラス(列挙値)オブジェクトは、メソッドを呼び出すときに同じ動作をします(メソッドボディが完全に同じであるため).各列挙オブジェクトがメソッドを呼び出すときに異なる動作方程式を提示する必要がある場合は、各列挙オブジェクトにそれぞれメソッドを実装させることができます.
public interface PrintColor {
    void print();
}

public enum Color implements PrintColor {
    RED {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    BLUE {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    BLACK {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    YELLOW {
        @Override
        public void print() {
            System.out.println("   !");
        }
    };
}

RED、BLUE、BLACK、およびYELLOWは、実際には、Colorクラスのインスタンスではなく、Color匿名サブクラスのインスタンスである.私の理解では、この時Colorは抽象的なクラスになりました.
注意:すべての列挙クラスがfinal修飾を使用しているわけではありません.非抽象的な列挙クラスはfinal修飾をデフォルトで使用します.抽象的な列挙クラス(抽象メソッドが含まれている限り抽象的な列挙クラス)では、final修飾ではなくabstract修飾がデフォルトで使用されます.
各列挙オブジェクトは、異なる列挙オブジェクトがメソッドを呼び出すときに異なる動作を有するように、異なる実装形態を提供する.
4.インタフェースを使用せずに、列挙クラスで抽象的なメソッドを直接定義し、各列挙オブジェクトに異なる実装方法を提供する.
public enum Color {
    RED {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    BLUE {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    BLACK {
        @Override
        public void print() {
            System.out.println("   !");
        }
    },
    YELLOW {
        @Override
        public void print() {
            System.out.println("   !");
        }
    };
    abstract void print();
}

Colorは抽象メソッドを含む列挙クラスですが、「public enum Color」ではabstract修飾は使用されません.これは、コンパイラ(javac)が.classをコンパイル生成するときにabstractを自動的に追加するためです.また、Colorには抽象メソッドが含まれているため、すべての列挙オブジェクトが抽象メソッドを実装する必要があります.そうしないと、コンパイラはエラーを報告します.
また、実際の使用では、例えば、public void modifyListing(List itemIds, Long status) public void modifyListing(List itemIds, MallItemStatusEnum status)という2つの方法は同じ方法であり、パラメータの1つはLongタイプを使用し、もう1つはMallItemStatusEnum列挙タイプを使用しています.違いのメリット:前者は、勝手に1つの値を伝えてもいいです.実際にそうすれば、この値に問題がないことを確認するために、方法の中で検証を行うに違いありません.後者は必要なく,statusが空であるか否かを判断する最大1つの操作を行う.
付録:java.lang.Enum
/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

/**
 * This is the common base class of all Java language enumeration types.
 *
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @version %I%, %G%
 * @since   1.5
 */
public abstract class Enum<E extends Enum<E>>
        implements Comparable, Serializable {
    /**
     * The name of this enum constant, as declared in the enum declaration.
     * Most programmers should use the {@link #toString} method rather than
     * accessing this field.
     */
    private final String name;

    /**
     * Returns the name of this enum constant, exactly as declared in its
     * enum declaration.
     * 
     * Most programmers should use the {@link #toString} method in
     * preference to this one, as the toString method may return
     * a more user-friendly name.  This method is designed primarily for
     * use in specialized situations where correctness depends on getting the
     * exact name, which will not vary from release to release.
     *
     * @return the name of this enum constant
     */
    public final String name() {
    return name;
    }

    /**
     * The ordinal of this enumeration constant (its position
     * in the enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     * 
     * Most programmers will have no use for this field.  It is designed
     * for use by sophisticated enum-based data structures, such as
     * {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     */
    private final int ordinal;

    /**
     * Returns the ordinal of this enumeration constant (its position
     * in its enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     * 
     * Most programmers will have no use for this method.  It is
     * designed for use by sophisticated enum-based data structures, such
     * as {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     *
     * @return the ordinal of this enumeration constant
     */
    public final int ordinal() {
    return ordinal;
    }

    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to
     * enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
    }

    /**
     * Returns the name of this enum constant, as contained in the
     * declaration.  This method may be overridden, though it typically
     * isn't necessary or desirable.  An enum type should override this
     * method when a more "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
    public String toString() {
    return name;
    }

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }

    /**
     * Returns a hash code for this enum constant.
     *
     * @return a hash code for this enum constant.
     */
    public final int hashCode() {
        return super.hashCode();
    }

    /**
     * Throws CloneNotSupportedException.  This guarantees that enums
     * are never cloned, which is necessary to preserve their "singleton"
     * status.
     *
     * @return (never returns)
     */
    protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
    }

    /**
     * Compares this enum with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     * 
     * Enum constants are only comparable to other enum constants of the
     * same enum type.  The natural order implemented by this
     * method is the order in which the constants are declared.
     */
    public final int compareTo(E o) {
    Enum other = (Enum)o;
    Enum self = this;
    if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
    }

    /**
     * Returns the Class object corresponding to this enum constant's
     * enum type.  Two enum constants e1 and  e2 are of the
     * same enum type if and only if
     *   e1.getDeclaringClass() == e2.getDeclaringClass().
     * (The value returned by this method may differ from the one returned
     * by the {@link Object#getClass} method for enum constants with
     * constant-specific class bodies.)
     *
     * @return the Class object corresponding to this enum constant's
     *     enum type
     */
    public final Class getDeclaringClass() {
    Class clazz = getClass();
    Class zuper = clazz.getSuperclass();
    return (zuper == Enum.class) ? clazz : zuper;
    }

    /**
     * Returns the enum constant of the specified enum type with the
     * specified name.  The name must match exactly an identifier used
     * to declare an enum constant in this type.  (Extraneous whitespace
     * characters are not permitted.) 
     *
     * @param enumType the Class object of the enum type from which
     *      to return a constant
     * @param name the name of the constant to return
     * @return the enum constant of the specified enum type with the
     *      specified name
     * @throws IllegalArgumentException if the specified enum type has
     *         no constant with the specified name, or the specified
     *         class object does not represent an enum type
     * @throws NullPointerException if enumType or name
     *         is null
     * @since 1.5
     */
    public static extends Enum> 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 const " + enumType +"." + name);
    }

    /**
      * prevent default deserialization
      */
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
            throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    /**
     * enum classes cannot have finalize methods.
     */
    protected final void finalize() { }
}