web-dev-qa-db-fra.com

Est-ce une bonne pratique d'utiliser ordinal of enum?

J'ai un enum:

public enum Persons {

    CHILD,
    PARENT,
    GRANDPARENT;

}

Existe-t-il un problème avec l'utilisation de la méthode ordinal() pour vérifier la "hiérarchie" entre les membres enum? Je veux dire - y a-t-il des inconvénients à l’utiliser, à l’exclusion de la verbosité, lorsque l’on peut changer d’ordre par accident à l’avenir.

Ou vaut-il mieux faire quelque chose comme ça:

public enum Persons {

    CHILD(0),
    PARENT(1),
    GRANDPARENT(2);

    private Integer hierarchy;

    private Persons(final Integer hierarchy) {
        this.hierarchy = hierarchy;
    }

    public Integer getHierarchy() {
        return hierarchy;
    }

}
25
ByeBye

Si vous vous référez à la méthode javadoc pour ordinal dans Enum.Java:

La plupart des programmeurs n'auront aucune utilité pour cette méthode. Il est conçu pour être utilisé par des structures de données sophistiquées basées sur enum, telles que Java.util.EnumSet et Java.util.EnumMap.

Tout d'abord - lisez le manuel (javadoc dans ce cas).

Deuxièmement, n'écrivez pas un code fragile. Les valeurs enum peuvent changer dans le futur et votre deuxième exemple de code est beaucoup plus clear et maintenable.

Vous ne voulez certainement pas créer de problèmes pour l’avenir si une nouvelle valeur enum est insérée (par exemple) entre PARENT et GRANDPARENT.

41
vikingsteve

La première façon n'est pas tout à fait compréhensible car vous devez lire le code où les énumérations sont utilisées pour comprendre que l’ordre de l’énumération est important.
Il est très sujet aux erreurs.

public enum Persons {

    CHILD,
    PARENT,
    GRANDPARENT;

}

La deuxième façon est meilleur tel quel explicite:

CHILD(0),
PARENT(1),
GRANDPARENT(2);

private SourceType(final Integer hierarchy) {
    this.hierarchy = hierarchy;
}

Bien entendu, les ordres des valeurs enum doivent être cohérents avec l'ordre hiérarchique fourni par les arguments du constructeur enum.

Il introduit une sorte de redondance car à la fois les valeurs enum et les arguments du constructeur enum en transmet la hiérarchie.
Mais pourquoi cela poserait-il problème?
Les énumérations sont conçues pour représenter des valeurs constantes et qui ne changent pas fréquemment.
L’utilisation de l’énumération OP illustre bien une bonne utilisation de l’énumération:

CHILD, PARENT, GRANDPARENT

Les énumérations ne sont pas conçues pour représenter des valeurs qui se déplacent fréquemment.
Dans ce cas, l’utilisation d’énums n’est probablement pas le meilleur choix car elle rompt fréquemment le code client qui l’utilise et oblige en outre à recompiler, à reconditionner et à redéployer l’application à chaque modification d’une valeur enum.

10
davidxxx

Comme suggéré par Joshua Bloch dans Effective Java, ce n'est pas une bonne idée de dériver une valeur associée à une enum de son ordinal, car des modifications dans l'ordre des valeurs d'énum pourraient casser la logique que vous avez codée.

La deuxième approche que vous mentionnez suit exactement ce que propose l'auteur, à savoir stocker la valeur dans un champ séparé.

Je dirais que l’alternative que vous avez suggérée est définitivement meilleure car elle est plus extensible et plus facile à gérer, car vous découplez l’ordre des valeurs enum et la notion de hiérarchie.

7
msf1013

Tout d'abord, vous n'avez probablement même pas besoin d'une valeur d'ordre numérique - c'est ce que Comparable est pour, et Enum<E> Implémente Comparable<E>.

Si vous do avez besoin d'une valeur d'ordre numérique pour une raison quelconque, oui, vous devriez utiliser ordinal(). C'est pour ça.

La pratique standard pour Java Enums consiste à trier par ordre de déclaration, raison pour laquelle Enum<E> Implémente Comparable<E> Et pourquoi Enum.compareTo() est final.

Si vous ajoutez votre propre code de comparaison non standard qui n'utilise pas Comparable et ne dépend pas de l'ordre de déclaration, vous allez confondre les autres personnes qui essaient d'utiliser votre code, y compris le vôtre. moi futur. Personne ne s'attendra à ce que ce code existe; ils vont s'attendre à ce que Enum soit Enum.

Si l'ordre personnalisé ne correspond pas à l'ordre de déclaration, les personnes qui consulteront la déclaration seront confondues. Si cela fait (à ce moment-là) correspond à l'ordre de déclaration, tous ceux qui le regarderont s'attendent à ce qu'ils l'attendent, et ils vont subir un choc désagréable quand date future ce n'est pas. (Si vous écrivez du code (ou des tests) pour assure que l'ordre personnalisé correspond à l'ordre de la déclaration, vous ne faites que renforcer son inutilité.)

Si vous ajoutez votre propre valeur de commande, vous créez des problèmes de maintenance:

  1. vous devez vous assurer que vos valeurs hierarchy sont uniques
  2. si vous ajoutez une valeur au milieu, vous devez renuméroter toutes les valeurs suivantes.

Si vous craignez que quelqu'un ne modifie la commande accidentellement à l'avenir, écrivez un test unitaire qui vérifie la commande.

En résumé, dans les mots immortels de élément 47 : connaître et utiliser les bibliothèques.


P.S. Aussi, n'utilisez pas Integer lorsque vous voulez dire int. ????

6
David Moles

L'utilisation de ordinal() n'est pas recommandée car des modifications dans la déclaration de l'énum peuvent avoir une incidence sur les valeurs ordinales.

PDATE:

Il est à noter que les champs enum sont des constantes et peuvent avoir des valeurs dupliquées, c'est-à-dire.

enum Family {
    OFFSPRING(0),
    PARENT(1),
    GRANDPARENT(2),
    SIBLING(3),
    COUSING(4),
    UNCLE(4),
    AUNT(4);

    private final int hierarchy;

    private Family(int hierarchy) {
        this.hierarchy = hierarchy;
    }

    public int getHierarchy() {
        return hierarchy;
    }
}

Selon ce que vous envisagez de faire avec hierarchy, cela pourrait être dommageable ou bénéfique.

De plus, vous pouvez utiliser les constantes enum pour construire votre propre EnumFlags au lieu d’utiliser EnumSet, par exemple

5
Leonardo Pina

Si vous voulez seulement créer des relations entre les valeurs enum, vous pouvez utiliser l’astuce d’utiliser autres valeurs enum:

public enum Person {
  GRANDPARENT(null),
  PARENT(GRANDPARENT),
  CHILD(PARENT);

  private final Person parent;

  private Person(Person parent) {
    this.parent = parent;
  }

  public final Parent getParent() {
    return parent;
  }
}

Notez que vous ne pouvez utiliser que les valeurs enum déclarées lexicalement avant celle que vous tentez de déclarer. Cela ne fonctionne donc que si vos relations forment un graphe dirigé acyclique (et que l'ordre que vous les déclarez est un tri topologique valide).

5
Daniel Pryden

J'utiliserais votre deuxième option (en utilisant un entier explicite) afin que les valeurs numériques soient attribuées par vous et non par Java.

3
gpeche

Ce n'est pas une réponse directe à votre question. Plutôt meilleure approche pour votre cas d'utilisation. De cette manière, le prochain développeur saura explicitement que les valeurs attribuées aux propriétés ne doivent pas être modifiées.

Créez une classe avec des propriétés statiques qui simuleront votre enum:

public class Persons {
    final public static int CHILD = 0;
    final public static int PARENT = 1;
    final public static int GRANDPARENT = 2;
}

Alors utilisez juste comme enum:

Persons.CHILD

Cela fonctionnera pour la plupart des cas d'utilisation simples. Sinon, des options telles que valueOf () , EnumSet , EnumMap ou values ​​() , risquent de manquer.

1
mzmrk

Selon Java doc

Renvoie l'ordinal de cette constante d'énumération (sa position dans sa déclaration enum, où la constante initiale se voit attribuer un ordinal égal à zéro). La plupart des programmeurs n'auront aucune utilité pour cette méthode. Il est conçu pour être utilisé par des structures de données sophistiquées telles que EnumSet et EnumMap.

Vous pouvez contrôler l'ordinal en modifiant l'ordre du enum, mais vous ne pouvez pas le définir explicitement. Une solution consiste à fournir une méthode supplémentaire dans votre enum pour le nombre souhaité.

enum Mobile {
   Samsung(400), Nokia(250),Motorola(325);

   private final int val;
  private Mobile (int v) { val = v; }
  public int getVal() { return val; }
}

Dans cette situation, Samsung.ordinal() = 0, mais Samsung.getVal() = 400.

1
gati sahu