Comment définissez-vous une classe de constantes en Java?

89

Supposons que vous ayez besoin de définir une classe qui ne fait que contenir des constantes.

public static final String SOME_CONST = "SOME_VALUE";

Quelle est la meilleure façon de procéder?

  1. Interface
  2. Classe abstraite
  3. Classe finale

Lequel dois-je utiliser et pourquoi?


Clarifications à certaines réponses:

Enums - Je ne vais pas utiliser d'énumérations, je n'énumère rien, je collecte juste des constantes qui ne sont en aucun cas liées entre elles.

Interface - Je ne vais pas définir une classe comme une qui implémente l'interface. Je veux juste utiliser l'interface pour appeler des constantes comme ceci: ISomeInterface.SOME_CONST.

Yuval Adam
la source
Il y a une discussion similaire ici: stackoverflow.com/questions/320588/… . J'utiliserais une classe finale avec un constructeur privé afin qu'elle ne puisse pas être instanciée.
Dan Dyer
2
Désolé mais "je ne vais pas utiliser d'énumérations" transforme cette question en "quelle est la meilleure façon de faire quelque chose de stupide?"
cletus
Je ne dis pas que vous allez implémenter l'interface. Mais il ne sert à rien d'utiliser une interface pour cela. Alors, allez avec la classe finale
:)
Quel est le problème avec Enum? Vous pouvez toujours l'utiliser pour collecter «des constantes qui ne sont en aucun cas liées les unes aux autres». Hmm?
gedevan le
5
Conceptuellement, une énumération est un mauvais choix si les constantes ne sont pas liées. Une énumération représente des valeurs alternatives du même type. Ces constantes ne sont pas des alternatives et elles peuvent même ne pas être du même type (certaines peuvent être des chaînes, des entiers, etc.)
Dan Dyer

Réponses:

92

Utilisez un dernier cours. pour plus de simplicité, vous pouvez ensuite utiliser une importation statique pour réutiliser vos valeurs dans une autre classe

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

dans une autre classe:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
user54579
la source
4
Où est l'avantage de créer une classe distincte ici? Bien que normalement je ne considère pas l'API Calendar comme un bon exemple de conception, c'est bien en termes de "constantes liées au calendrier sont dans la classe Calendar".
Jon Skeet
7
l'avantage est de ne pas dupliquer le code, au cas où vous auriez besoin de réutiliser des constantes dans plus d'une classe. Je suppose que vous pouvez facilement voir l'avantage de cela.
user54579
2
Pourquoi dupliqueriez-vous le code? Reportez-vous simplement à l'autre classe. Je trouve relativement rare qu'une constante soit vraiment isolée.
Jon Skeet
14
Pour une meilleure lisibilité, j'aurais également un constructeur par défaut privé sans corps (et un commentaire correspondant).
Ran Biron le
5
Réponse assez ancienne, et ce commentaire est probablement hors de propos, mais je l'utiliserais VALUE1.equals(variable)car il évite NPE.
Aakash
38

Votre clarification stipule: "Je ne vais pas utiliser d'énumérations, je n'énumère rien, je ne fais que collecter des constantes qui ne sont en aucun cas liées les unes aux autres."

Si les constantes ne sont pas du tout liées les unes aux autres, pourquoi voulez-vous les rassembler? Mettez chaque constante dans la classe à laquelle elle est le plus étroitement liée.

Jon Skeet
la source
2
Jon - ils sont liés dans le sens où ils appartiennent tous à la même fonctionnalité. Cependant, ils ne sont pas une énumération de quoi que ce soit ... Ils ne détiennent pas la propriété qu'un objet est "l'une de ces choses".
Yuval Adam le
13
D'accord. Dans ce cas, placez-les simplement dans la classe la plus proche de la fonctionnalité à laquelle ils sont liés.
Jon Skeet
26

Mes suggestions (par ordre décroissant de préférence):

1) Ne le fais pas . Créez les constantes dans la classe réelle là où elles sont les plus pertinentes. Avoir une classe / interface «sac de constantes» ne suit pas vraiment les meilleures pratiques OO.

Moi et tout le monde, nous ignorons le n ° 1 de temps en temps. Si vous allez faire cela, alors:

2) classe finale avec constructeur privé Cela empêchera au moins quiconque d'abuser de votre «sac de constantes» en l'étendant / l'implémentant pour avoir un accès facile aux constantes. (Je sais que tu as dit que tu ne ferais pas ça - mais ça ne veut pas dire que quelqu'un vient après toi)

3) interface Cela fonctionnera, mais pas ma préférence en donnant la mention d'abus possible au n ° 2.

En général, ce n'est pas parce que ce sont des constantes que vous ne devriez pas toujours leur appliquer les principes oo normaux. Si personne d'autre qu'une classe ne se soucie d'une constante - elle doit être privée et dans cette classe. Si seuls les tests se soucient d'une constante, elle doit être dans une classe de test, pas dans du code de production. Si une constante est définie à plusieurs endroits (pas simplement accidentellement les mêmes) - refactoriser pour éliminer la duplication. Et ainsi de suite - traitez-les comme une méthode.

kenj0418
la source
2
Le problème du 1 est que parfois vous injecterez dans votre code des dépendances à une autre classe de niveau juste pour récupérer la constante.
amdev
Tous les champs d'une interface sont implicitement publics, statiques et définitifs
Kodebetter
@Kodebetter Mais les interfaces peuvent être étendues / implémentées pour ajouter plus de champs.
Wit
12

Comme le note Joshua Bloch dans Effective Java:

  • Les interfaces ne doivent être utilisées que pour définir des types,
  • les classes abstraites n'empêchent pas l'instanciabilité (elles peuvent être sous-classées, et suggèrent même qu'elles sont conçues pour être sous-classées).

Vous pouvez utiliser un Enum si toutes vos constantes sont liées (comme les noms de planètes), mettre les valeurs constantes dans les classes auxquelles elles sont liées (si vous y avez accès), ou utiliser une classe utilitaire non instanciable (définir un constructeur par défaut privé) .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

Ensuite, comme déjà indiqué, vous pouvez utiliser des importations statiques pour utiliser vos constantes.

Sébastien RoccaSerra
la source
7

Ma méthode préférée est de ne pas le faire du tout. L'âge des constantes est pratiquement mort lorsque Java 5 a introduit les énumérations typesafe. Et même avant, Josh Bloch a publié une version (un peu plus verbeuse) de cela, qui fonctionnait sur Java 1.4 (et les versions antérieures).

À moins que vous n'ayez besoin d'une interopérabilité avec du code hérité, il n'y a plus aucune raison d'utiliser des constantes String / integer nommées.

cletus
la source
2
Bonne réponse, un exemple serait mieux.
amdev
3

Utilisez simplement la classe finale.

Si vous souhaitez pouvoir ajouter d'autres valeurs, utilisez une classe abstraite.

Cela n'a pas beaucoup de sens d'utiliser une interface, une interface est censée spécifier un contrat. Vous souhaitez simplement déclarer des valeurs constantes.

Megacan
la source
3

Les énumérations ne sont-elles pas le meilleur choix pour ce genre de choses?

Boris Pavlović
la source
2
99% du temps. Mais pas toujours.
L. Holanda
3

enums vont bien. IIRC, un élément en Java effectif (2e éd.) A des enumconstantes énumérant les options standard implémentant un [mot-clé Java] interfacepour n'importe quelle valeur.

Ma préférence est d'utiliser un [mot-clé Java] interfaceplutôt qu'un final classpour les constantes. Vous obtenez implicitement le public static final. Certaines personnes soutiendront qu'un interfacepermet aux mauvais programmeurs de l'implémenter, mais les mauvais programmeurs vont écrire du code qui est nul, quoi que vous fassiez.

Qu'est-ce qui a l'air mieux?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

Ou:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Tom Hawtin - Tacle
la source
mais les mauvais programmeurs vont écrire du code qui craint peu importe ce que vous faites. C'est tellement vrai. Si vous pouvez prendre des mesures pour atténuer la mauvaise programmation, ou l'éliminer complètement, alors vous avez un peu la responsabilité de le faire; soyez aussi explicite que possible. Un mauvais programmeur ne peut pas étendre une classe finale.
liltitus27
1
@ liltitus27 Dans une certaine mesure, je suis d'accord. Mais voulez-vous vraiment du code uglifié juste pour arrêter une façon de pauvres programmeurs d'écrire du mauvais code? Le code qu'ils produisent sera toujours sans espoir, mais maintenant votre code est moins lisible.
Tom Hawtin - tackline
1

Ou 4. Placez-les dans la classe qui contient la logique qui utilise le plus les constantes

... désolé, je n'ai pas pu résister ;-)

Simon Groenewolt
la source
3
Ce n'est pas une bonne idée. Cela crée des dépendances entre les classes qui ne devraient en avoir aucune.
Yuval Adam le
Pourquoi pas? Je dirais que les classes qui utilisent les mêmes constantes ont de toute façon une dépendance ... Et d'une manière ou d'une autre, il doit y avoir une classe qui est la plus dépendante de ces constantes.
Simon Groenewolt
S'il n'y avait qu'une seule classe utilisant les constantes, cela pourrait avoir du sens.
Tom Hawtin - tackline
-1
  1. L'un des inconvénients du constructeur privé est que la méthode existe ne peut jamais être testée.

  2. Enum par le concept de nature bon à appliquer dans un type de domaine spécifique, l'appliquer à des constantes décentralisées ne semble pas assez bon

Le concept d'énumération est «les énumérations sont des ensembles d'éléments étroitement liés».

  1. Étendre / implémenter une interface constante est une mauvaise pratique, il est difficile de penser à l'exigence d'étendre une constante immuable au lieu de s'y référer directement.

  2. Si vous appliquez un outil de qualité comme SonarSource, il existe des règles obligeant le développeur à abandonner l'interface constante, c'est une chose gênante car beaucoup de projets apprécient l'interface constante et voient rarement les choses «étendre» se produire sur des interfaces constantes

Guizhou Feng
la source
1
"constructeur privé est l'existence d'une méthode qui n'a jamais pu être testée": non vrai. Si vous (ou votre patron) êtes vraiment paranoïaque à propos des rapports de couverture de code à 100%, vous pouvez toujours le tester en utilisant la réflexion et en vous assurant que le constructeur lève une exception. Mais, à mon humble avis, ce n'est que formalité et n'a pas vraiment besoin d'être testé. Si vous (ou votre équipe) ne vous souciez vraiment que de ce qui est vraiment important, une couverture de ligne à 100% n'est pas nécessaire.
L. Holanda