118 votes

Interfaces avec champs statiques en java pour le partage des "constantes".

Je suis en train de regarder quelques projets Java open source pour me familiariser avec Java et je remarque que beaucoup d'entre eux ont une sorte d'interface de "constantes".

Par exemple, traitement.org possède une interface appelée PConstants.java et la plupart des autres classes principales mettent en œuvre cette interface. L'interface est truffée de membres statiques. Y a-t-il une raison pour cette approche, ou est-ce considéré comme une mauvaise pratique ? Pourquoi ne pas utiliser les enums où cela a du sens ou une classe statique ?

Je trouve étrange d'utiliser une interface pour permettre une sorte de pseudo "variables globales".

public interface PConstants {

  // LOTS OF static fields...

  static public final int SHINE = 31;

  // emissive (by default kept black)
  static public final int ER = 32;
  static public final int EG = 33;
  static public final int EB = 34;

  // has this vertex been lit yet
  static public final int BEEN_LIT = 35;

  static public final int VERTEX_FIELD_COUNT = 36;

  // renderers known to processing.core

  static final String P2D    = "processing.core.PGraphics2D";
  static final String P3D    = "processing.core.PGraphics3D";
  static final String JAVA2D = "processing.core.PGraphicsJava2D";
  static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
  static final String PDF    = "processing.pdf.PGraphicsPDF";
  static final String DXF    = "processing.dxf.RawDXF";

  // platform IDs for PApplet.platform

  static final int OTHER   = 0;
  static final int WINDOWS = 1;
  static final int MACOSX  = 2;
  static final int LINUX   = 3;

  static final String[] platformNames = {
    "other", "windows", "macosx", "linux"
  };

  // and on and on

}

15 votes

Note : static final n'est pas nécessaire, il est redondant pour une interface.

0 votes

Notez également que platformNames peut être public , static y final mais ce n'est certainement pas une constante. Le seul tableau constant est celui dont la longueur est nulle.

0 votes

@ThomasW Je sais que cela date de quelques années, mais je devais signaler une erreur dans votre commentaire. static final n'est pas nécessairement redondant. Un champ de classe ou d'interface avec seulement le final permet de créer des instances distinctes de ce champ au fur et à mesure que vous créez des objets de la classe ou de l'interface. Utilisation de static final ferait en sorte que chaque objet partage un emplacement mémoire pour ce champ. En d'autres termes, si une classe MyClass possède un champ final String str = "Hello"; Ainsi, pour N instances de MyClass, il y aurait N instances du champ str en mémoire. En ajoutant le static ne donnerait lieu qu'à une seule instance.

164voto

Dan Dyer Points 30082

C'est généralement considéré comme une mauvaise pratique. Le problème est que les constantes font partie de l'"interface" publique (faute d'un meilleur terme) de la classe d'implémentation. Cela signifie que la classe d'implémentation publie toutes ces valeurs à des classes externes, même si elles ne sont requises qu'en interne. Les constantes prolifèrent dans tout le code. Un exemple est le SwingConstants dans Swing, qui est mise en œuvre par des dizaines de classes qui "réexportent" toutes l'interface suivante tous de ses constantes (même celles qu'ils n'utilisent pas) comme les leurs.

Mais ne me croyez pas sur parole, Josh Bloch dit aussi c'est mauvais :

Le modèle d'interface constant est une mauvaise utilisation des interfaces. Le fait qu'une classe utilise certaines constantes en interne est un détail d'implémentation. L'implémentation d'une interface de constantes entraîne une fuite de ce détail d'implémentation dans l'API exportée de la classe. Il n'y a aucune conséquence pour les utilisateurs d'une classe que celle-ci implémente une interface constante. En fait, cela peut même les perturber. Pire, cela représente un engagement : si, dans une version ultérieure, la classe est modifiée de telle sorte qu'elle n'a plus besoin d'utiliser les constantes, elle doit quand même implémenter l'interface pour assurer la compatibilité binaire. Si une classe non définitive implémente une interface constante, toutes ses sous-classes verront leurs espaces de noms pollués par les constantes de l'interface.

Un enum peut être une meilleure approche. Ou vous pourriez simplement placer les constantes en tant que champs statiques publics dans une classe qui ne peut pas être instanciée. Cela permet à une autre classe d'y accéder sans polluer sa propre API.

1 votes

Il convient de noter que dans certains cas, vous n'avez pas le choix, par exemple JavaME.

8 votes

Les Enums sont un hareng rouge ici - ou du moins une question distincte. Les Enums doivent bien sûr être utilisés, mais ils doivent aussi être cachés s'ils ne sont pas nécessaires aux implémenteurs.

12 votes

BTW : Vous pouvez utiliser un enum sans instances comme une classe qui ne peut pas être instanciée ;)

103voto

Zarkonnen Points 11086

Au lieu de mettre en œuvre une "interface de constantes", en Java 1.5+, vous pouvez utiliser les importations statiques pour importer les constantes/méthodes statiques d'une autre classe/interface :

import static com.kittens.kittenpolisher.KittenConstants.*;

Cela permet d'éviter que vos classes n'implémentent des interfaces qui n'ont aucune fonctionnalité.

Quant à la pratique consistant à avoir une classe uniquement pour stocker les constantes, je pense que c'est parfois nécessaire. Il y a certaines constantes qui n'ont tout simplement pas leur place naturelle dans une classe, il est donc préférable de les avoir dans un endroit "neutre".

Mais au lieu d'utiliser une interface, utilisez une classe finale avec un constructeur privé. (Rendant impossible l'instanciation ou la sous-classification de la classe, envoyant un message fort qu'elle ne contient pas de fonctionnalité/données non statiques).

Eg :

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}

0 votes

Donc, vous êtes en train d'expliquer que, à cause de l'importation statique, nous devrions utiliser des classes au lieu d'intefaces pour refaire la même erreur qu'avant ! C'est stupide !

13 votes

Non, ce n'est pas du tout ce que je dis. Je dis deux choses indépendantes. 1 : utilisez les importations statiques au lieu d'abuser de l'héritage. 2 : Si vous devez avoir un dépôt de constantes, faites-en une classe finale au lieu d'une interface.

0 votes

Les "interfaces constantes" n'ont pas été conçues pour faire partie d'un quelconque héritage, jamais. Donc l'import statique est juste un sucre syntaxique, et hériter d'une telle interface est une erreur horrible. Je sais que Sun a fait ça, mais ils ont aussi fait des tonnes d'autres erreurs de base, ce n'est pas une excuse pour les imiter.

9voto

Уmed Points 3410

Je ne prétends pas avoir raison, mais voyons ce petit exemple :

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

ToyotaCar ne sait rien de FordCar, et FordCar ne sait rien de ToyotaCar. Le principe CarConstants devrait être modifié, mais...

Les constantes ne doivent pas être modifiées, car la roue est ronde et l'égine est mécanique, mais... Dans le futur, les ingénieurs de recherche de Toyota ont inventé le moteur électronique et les roues plates ! Voyons notre nouvelle interface

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

et maintenant nous pouvons changer notre abstraction :

public interface ToyotaCar extends CarConstants

à

public interface ToyotaCar extends InnovativeCarConstants 

Et maintenant, si nous devons changer la valeur fondamentale du MOTEUR ou de la ROUE, nous pouvons modifier l'interface ToyotaCar au niveau de l'abstraction, sans toucher aux implémentations.

Ce n'est PAS SÛR, je sais, mais je veux quand même savoir ce que vous pensez de ça.

0 votes

Je veux connaître votre idée maintenant en 2019. Pour moi, les champs d'interface sont destinés à être partagés entre certains objets.

0 votes

J'ai écrit une réponse en rapport avec votre idée : stackoverflow.com/a/55877115/5290519

0 votes

C'est un bon exemple de la façon dont les règles de PMD sont très limitées. Essayer d'obtenir un meilleur code par le biais de la bureaucratie reste une tentative futile.

6voto

Avec le recul, nous pouvons constater que Java est cassé à bien des égards. L'un des principaux défauts de Java est la restriction des interfaces aux méthodes abstraites et aux champs statiques finaux. Des langages OO plus récents et plus sophistiqués comme Scala subsument les interfaces par des traits qui peuvent inclure (et incluent généralement) des méthodes concrètes, qui peuvent avoir une arité zéro (constantes !). Pour un exposé sur les traits en tant qu'unités de comportement composable, voir http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf . Pour une brève description de la comparaison entre les traits en Scala et les interfaces en Java, voir http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5 . Dans le contexte de l'enseignement de la conception OO, les règles simplistes telles que l'affirmation selon laquelle les interfaces ne devraient jamais inclure de champs statiques sont stupides. De nombreux traits incluent naturellement des constantes et ces constantes font convenablement partie de l'"interface" publique supportée par le trait. Dans l'écriture du code Java, il n'existe pas de moyen propre et élégant de représenter les traits, mais l'utilisation de champs statiques finaux dans les interfaces fait souvent partie d'une bonne solution de contournement.

13 votes

Terriblement prétentieux et aujourd'hui dépassé.

1 votes

Merveilleux aperçu (+1), bien que peut-être un peu trop critique envers Java.

1voto

om39a Points 646

Selon les spécifications de la JVM, les champs et les méthodes d'une interface ne peuvent être que Public, Static, Final et Abstract. Réf. de l'intérieur de Java VM

Par défaut, toutes les méthodes de l'interface sont abstraites, même si vous ne l'avez pas mentionné explicitement.

Les interfaces sont censées ne donner que des spécifications. Elles ne peuvent pas contenir d'implémentations. Ainsi, pour éviter que les classes d'implémentation ne modifient la spécification, elle est rendue finale. Puisque les interfaces ne peuvent pas être instanciées, elles sont rendues statiques pour accéder au champ en utilisant le nom de l'interface.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X