web-dev-qa-db-ja.com

Java enumとプライベートコンストラクターを持つクラスの違いは何ですか?

私はJava enumが実際にどのように機能するかを理解しようとしていましたが、コンストラクタが宣言されている通常のJavaクラスに非常に似ているという結論に達しましたプライベート。

私はこの結論にたどり着きましたが、それはあまり考えに基づいたものではありませんが、何かを逃したかどうかを知りたいのです。

以下は、単純なJava enumと同等のJavaクラスの実装です。

public enum Direction {
    ENUM_UP(0, -1),
    ENUM_DOWN(0, 1),
    ENUM_RIGHT(1, 0),
    ENUM_LEFT(-1, 0);


    private int x;
    private int y;

    private Direction(int x, int y){
        this.x = x;
        this.y = y;
    }
    public int getEnumX(){
        return x;
    }
    public int getEnumY(){
        return y;
    }
}

上記のコードと以下のコードの意味の違いは何ですか?

public class Direction{
    public static final Direction UP = new Direction(0, -1) ;
    public static final Direction DOWN = new Direction(0, 1) ;
    public static final Direction LEFT = new Direction(-1, 0) ;
    public static final Direction RIGHT = new Direction(1, 0) ;


    private int x ;
    private int y ;

    private Direction(int x, int y){
        this.x = x ;
        this.y = y ;
    }
    public int getX(){
        return x;
    }
    public int getY(){
        return y;
    }
}
57
user9349193413

違い:

  1. 列挙型は_Java.lang.Enum_を拡張し、そのすべてを取得します 素晴らしい機能
    1. 正しいシリアル化による自動シングルトン動作
    2. 列挙名を複製する必要のない、列挙値に対する人間が読める自動_.toString_メソッド
    3. _.name_および_.ordinal_特殊目的メソッド
    4. 高性能ビットセットベースのEnumSetおよびEnumMapクラスで使用可能
  2. 列挙型は言語によって特別に扱われます:
    1. 列挙型は、多数の_public static final_フィールドを書き込まずにインスタンスの作成を簡素化する特別な構文を使用します
    2. 列挙型はswitchステートメントで使用できます
    3. リフレクションを使用しない限り、列挙型リストの外で列挙型をインスタンス化することはできません
    4. 列挙型は列挙型リストの外に拡張できません
  3. Javaは自動的に余分なものを列挙型にコンパイルします:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(Java.lang.String);
    3. private static final (Enum)[] $VALUES;values()はこのクローンを返します)

これらのほとんどは適切に設計されたクラスでエミュレートできますが、Enumは、この特に望ましいプロパティのセットを使用してクラスを簡単に作成できるようにします。

58
nneonneo

質問に答えるには、基本的に、2つのアプローチに違いはありません。ただし、enumコンストラクトは、values()valueOf()などの追加のサポートメソッドを提供します。これらのメソッドは、private-with-private-constructorアプローチで独自に記述する必要があります。 。

しかし、ええ、私はJava enumはJavaの他のクラスとほとんど同じで、フィールド、動作などを持つことができます。しかし、私にとってenumとプレーンクラスを区別するのは列挙型は、インスタンス/メンバーが事前に決定されているクラス/タイプです。任意の数のインスタンスを作成できる通常のクラスとは異なり、列挙型は既知のインスタンスにのみ作成を制限します。コンストラクタですが、列挙型はこれをより直感的にするだけです。

10
Psycho Punch

このブログページ をご覧ください。Java enumsがバイトコードにコンパイルされる方法を説明しています。 2番目のコードサンプルは、Directionと呼ばれるVALUESオブジェクトの配列です。この配列は、enumのすべての可能な値を保持するため、実行できません

_new Direction(2, 2)
_

(たとえばリフレクションを使用して)、それを有効なDirection値として使用します。

さらに、@ Eng.Fouadが正しく説明しているように、values()valueOf()、およびordinal()はありません。

6
mthmulders

人々が指摘したように、あなたはvalues()valueOf()、およびordinal()を失います。 MapListの組み合わせを使用すると、この動作をかなり簡単に複製できます。

public class Direction {

    public static final Direction UP = build("UP", 0, -1);
    public static final Direction DOWN = build("DOWN", 0, 1);
    public static final Direction LEFT = build("LEFT", -1, 0);
    public static final Direction RIGHT = build("RIGHT", 1, 0);
    private static final Map<String, Direction> VALUES_MAP = new LinkedHashMap<>();
    private static final List<Direction> VALUES_LIST = new ArrayList<>();
    private final int x;
    private final int y;
    private final String name;

    public Direction(int x, int y, String name) {
        this.x = x;
        this.y = y;
        this.name = name;
    }

    private static Direction build(final String name, final int x, final int y) {
        final Direction direction = new Direction(x, y, name);
        VALUES_MAP.put(name, direction);
        VALUES_LIST.add(direction);
        return direction;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public static Direction[] values() {
        return VALUES_LIST.toArray(new Direction[VALUES_LIST.size()]);
    }

    public static Direction valueOf(final String direction) {
        if (direction == null) {
            throw new NullPointerException();
        }
        final Direction dir = VALUES_MAP.get(direction);
        if (dir == null) {
            throw new IllegalArgumentException();
        }
        return dir;
    }

    public int ordinal() {
        return VALUES_LIST.indexOf(this);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + name.hashCode();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Direction other = (Direction) obj;
        return name.equals(other.name);
    }

    @Override
    public String toString() {
        return name;
    }
}

ご覧のように;コードはすぐに非常に不格好になります。

このクラスでswitchステートメントを複製する方法があるかどうかはわかりません。あなたはそれを失います。

5

主な違いは、各enumクラスが暗黙的に_Enum<E extends Enum<E>>_クラスを拡張することです。これはそれにつながります:

  1. enumオブジェクトには、name()ordinal()などのメソッドがあります
  2. enumオブジェクトには、特別なtoString()hashCode()equals()、およびcompareTo()の実装があります
  3. enumオブジェクトは、switch演算子に適しています。

上記のすべては、Directionクラスのバージョンには適用されません。これが「意味」の違いです。

0
Andremoniy