J'essayais de comprendre comment Java enum fonctionne vraiment et je suis arrivé à la conclusion qu'il est très similaire à une classe Java normale avec son constructeur déclaré) privé.
Je viens d'arriver à cette conclusion et elle n'est pas basée sur beaucoup de réflexion, mais j'aimerais savoir si je manque quelque chose.
Voici donc une implémentation d'une simple énumération Java et d'une classe équivalente 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;
}
}
Quelle est la différence de sens entre le code ci-dessus et 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;
}
}
Différences:
Java.lang.Enum
Et gagnent toutes ses fonctionnalités intéressantes : .toString
Sur les valeurs d'énumération sans avoir à dupliquer vos noms d'énumération.name
Et .ordinal
Méthodes spécialesEnumSet
et EnumMap
basées sur un jeu de bits hautes performancespublic static final
switch
public static (Enum)[] values();
public static (Enum) valueOf(Java.lang.String);
private static final (Enum)[] $VALUES;
(values()
renvoie un clone de ceci)La plupart d'entre elles peuvent être émulées avec une classe convenablement conçue, mais Enum
facilite simplement la création d'une classe avec cet ensemble de propriétés particulièrement souhaitables.
Pour répondre à la question: essentiellement, il n'y a pas de différence entre les deux approches. Cependant, la construction enum vous fournit des méthodes de support supplémentaires comme values()
, valueOf()
, etc. que vous devriez écrire vous-même avec l'approche class-with-private-constructor .
Mais oui, j'aime comment Java sont pour la plupart comme toutes les autres classes de Java, elles peuvent avoir des champs, des comportements, etc. Mais pour moi, ce qui sépare les énumérations des classes ordinaires est l'idée que les énumérations sont des classes/types dont les instances/membres sont prédéterminés. Contrairement aux classes habituelles à partir desquelles vous pouvez créer un nombre illimité d'instances, les énumérations ne limitent la création qu'aux instances connues. Oui, comme vous l'avez illustré, vous pouvez également le faire avec des classes avec private constructeurs, mais les énumérations rendent cela plus intuitif.
Jetez un oeil à cette page de blog , il décrit comment Java enum
s sont compilés en bytecode. Vous verrez qu'il y a un petit ajout par rapport à votre deuxième exemple de code, qui est un tableau d'objets Direction
appelé VALUES
. Ce tableau contient toutes les valeurs possibles pour votre énumération, vous ne pourrez donc pas le faire
new Direction(2, 2)
(par exemple en utilisant la réflexion), puis utilisez-la comme une valeur Direction
valide.
De plus, comme @ Eng.Fouad l'explique correctement, vous n'avez pas values()
, valueOf()
et ordinal()
.
Comme certains l'ont souligné, vous perdez values()
, valueOf()
et ordinal()
. Vous pouvez reproduire ce comportement assez facilement en utilisant une combinaison de Map
et 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 sais pas s'il existe un moyen de répliquer une instruction switch
avec cette classe; vous allez donc perdre cela.
La principale différence est que chaque classe enum
étend implicitement la classe Enum<E extends Enum<E>>
. Cela conduit à cela:
enum
les objets ont des méthodes telles que name()
et ordinal()
enum
les objets ont des implémentations spéciales toString()
, hashCode()
, equals()
et compareTo()
enum
conviennent à l'opérateur switch
.Tous les éléments mentionnés ci-dessus ne s'appliquent pas à votre version de la classe Direction
. C'est la différence de "sens".