Quelles sont les différences entre un Java enum et une classe avec un constructeur privé? [dupliquer]

cette question a déjà une réponse ici:

  • Quel est l'avantage D'un Java enum par rapport à une classe avec des champs finaux publics statiques? 15 réponses

j'ai essayé de comprendre comment Java enum fonctionne vraiment et j'en suis venu à la conclusion qu'il est très similaire à un normal Classe Java dont le constructeur est déclaré privé.

je viens juste d'arriver à cette conclusion et elle n'est pas basée sur beaucoup de réflexion, mais J'aime savoir si je manque quelque chose.

donc ci-dessous est une implémentation D'un simple Java enum et d'une classe Java équivalente.

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;
    }
}

Quelle est la différence de signification entre le code ci-dessus et celui ci-dessous?

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;
    }
}
53
demandé sur urig 2013-04-01 00:33:26

5 réponses

Différences:

  1. Enums étendre java.lang.Enum et de gagner tout son nice features :
    1. Automatique singleton comportement grâce à un bon de sérialisation
    2. méthode automatique lisible par l'homme .toString sur les valeurs enum sans la nécessité de dupliquer vos noms enum
    3. .name et .ordinal méthodes spéciales
    4. Utilisable dans les classes EnumSet et EnumMap à base de bitset haute performance"
  2. les Énums sont traités par la langue spécialement:
    1. Enums utiliser une syntaxe spéciale qui simplifie la création d'instance sans écrire des dizaines de public static final champs
    2. les Énumérations peuvent être utilisés dans les switch états
    3. Enums ne peut être instancié en dehors de la liste d'énumération sauf en utilisant réflexion
    4. les Énumérations ne peut pas être étendu à l'extérieur de l'énumération de la liste
  3. Java compile automatiquement des éléments supplémentaires dans enums:
    1. public static (Enum)[] values();
    2. public static (Enum) valueOf(java.lang.String);
    3. private static final (Enum)[] $VALUES; ( values() renvoie un clone de ceci)

la plupart de ceux-ci peuvent être émulés avec un classe, mais Enum rend juste vraiment facile de créer une classe avec cet ensemble de propriétés particulièrement désirables.

52
répondu nneonneo 2013-03-31 21:45:51

pour répondre à la question: essentiellement, il n'y a pas de différence entre les deux approches. Cependant, enum construct vous fournit quelques méthodes supplémentaires de support comme values() , valueOf() , etc. que vous devez écrire vous-même avec l'approche de classe-avec-un-constructeur privé.

Mais oui, J'aime Comment Java enums sont la plupart du temps comme toutes les autres classes en Java, ils peuvent avoir des champs, des comportements, etc. Mais pour moi ce qui sépare les énums de la plaine les classes est l'idée que les enums sont des classes/types dont les instances/membres sont prédéterminés. Contrairement aux classes habituelles où vous pouvez créer n'importe quel nombre d'instances à partir de, enums limitent la création aux instances connues. Oui, comme vous l'avez illustré, vous pouvez également le faire avec des classes avec des constructeurs privés, mais enums juste rendre cela plus intuitif.

7
répondu Psycho Punch 2013-03-31 20:58:16

jetez un oeil à cette page de blog , il décrit comment Java enum s sont compilés dans bytecode. Vous verrez qu'il y a un petit ajout par rapport à votre second échantillon de code, qui est un tableau d'objets Direction appelé VALUES . Ce tableau contient toutes les valeurs possibles pour votre enum, donc vous ne pourrez pas faire

new Direction(2, 2)

(par exemple en utilisant la réflexion) et ensuite utiliser comme une valeur valide Direction .

de Plus, comme @Eng.Fouad explique correctement, vous n'avez pas values() , valueOf() et ordinal() .

6
répondu mthmulders 2013-03-31 20:40:46

comme les gens ont souligné vous perdez values() , valueOf() et ordinal() . Vous pouvez reproduire ce comportement assez facilement en utilisant une combinaison d'un Map et un List .

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;
    }
}

Comme vous pouvez le voir, le code devient très maladroit, très rapidement.

Je ne suis pas sûr qu'il y ait un moyen de répliquer une déclaration switch avec cette classe; donc vous perdrez cela.

4
répondu Boris the Spider 2013-03-31 21:03:34

la principale différence est que la classe enum s'étend implicitement à la classe Enum<E extends Enum<E>> . Cela conduit à cela:

  1. enum objets ont des méthodes telles que name() et ordinal()
  2. enum les objets ont spécial toString() , hashCode() , equals() et compareTo() implémentations
  3. enum les objets sont appropriés pour l'opérateur switch .
  4. Direction

    toutes les mentions ci-dessus ne s'appliquent pas à votre version de la classe Direction . C'est le "sens" de la différence.

0
répondu Andremoniy 2013-03-31 20:52:17