L '«interface statique» est-elle une bonne pratique?

13

J'ai récemment remarqué qu'il existe une option pour avoir des méthodes statiques dans les interfaces. Comme pour les champs d'interface statiques, il existe un comportement intéressant: ceux-ci ne sont pas hérités.

Je ne suis pas sûr que ce soit utile dans les interfaces réelles qui doivent être implémentées. Cependant, cela permet au programmeur de créer des interfaces qui ne sont que des enveloppes pour des éléments statiques, comme par exemple les classes utilitaires.

Un exemple simple est juste une enveloppe pour les constantes globales. Par rapport à une classe, vous pouvez facilement remarquer le passe-partout manquant public static finallorsque ceux-ci sont supposés (le rendant moins verbeux).

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

Vous pouvez également rendre quelque chose de plus complexe, comme ce pseudo-énumération de clés de configuration.

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

Vous pouvez également créer une "classe" utilitaire de cette façon. Cependant, dans les utilitaires, il est souvent utile d'utiliser des méthodes d'assistance privées ou protégées, ce qui n'est pas tout à fait possible dans les classes.

Je considère que c'est une nouvelle fonctionnalité intéressante, et surtout le fait que les membres statiques ne sont pas hérités est un concept intéressant qui a été introduit uniquement pour les interfaces.

Je me demande si vous pouvez considérer cela comme une bonne pratique. Bien que le style de code et les meilleures pratiques ne soient pas axiomatiques et qu'il y ait de la place pour l'opinion, je pense qu'il y a généralement des raisons valables pour appuyer l'opinion.

Je suis plus intéressé par les raisons (non) d'utiliser des modèles comme ceux-ci.


Notez que je n'ai pas l'intention d'implémenter ces interfaces. Ils ne sont qu'une enveloppe pour leur contenu statique. J'ai seulement l'intention d'utiliser les constantes ou les méthodes et éventuellement d'utiliser l'importation statique.

Vlasec
la source
1
Ma réaction réflexe à ce sujet est que cela ne peut que conduire à de mauvaises choses. Même si les méthodes statiques publiques ne sont essentiellement que des fonctions globales à espace de noms. C'est comme autoriser tout code d'implémentation dans une interface va à l'encontre de son objectif en tant que définition de contrat sans implémentation. Je dis laisser l'implémentation partielle aux classes abstraites. Je vais avoir besoin de lire pourquoi cela a été ajouté.
Adrian
Je dois y aller pour l'instant mais j'écrirai quelque chose demain dessus. Le plus court est qu'avant, une interface avait un objectif clair qui était clairement séparé de l'objectif d'une classe abstraite. Maintenant, ils se chevauchent fortement d'une manière qui viole la définition indépendante du langage d'une interface. De plus, il existe des cas où vous ne pouvez plus hériter de plusieurs interfaces si elles implémentent des méthodes par défaut. Alors maintenant, il existe des exceptions à l'héritage d'interfaces multiples où il n'y en avait pas auparavant. Depuis que Java s'est considérablement écarté de la définition commune d'une interface ...
Adrian
cela pourrait également dérouter les nouveaux développeurs qui essaient de maîtriser correctement les concepts fondamentaux de la POO tels que les interfaces, les AbstractClases, quand utiliser la composition au lieu de l'héritage, ... etc.
Adrian
Aliester, vous parlez de defaultméthodes. Je parle de staticméthodes et de domaines. Ceux-ci ne sont pas hérités, ils ne cassent donc pas l'héritage multiple.
Vlasec

Réponses:

1

Un exemple simple est juste une enveloppe pour les constantes globales.

Mettre des constantes sur les interfaces s'appelle le Constant Interface Antipatterncar les constantes ne sont souvent qu'un détail d'implémentation. D'autres inconvénients sont résumés dans l'article Wikipedia sur l' interface Constant .

Vous pouvez également créer une "classe" utilitaire de cette façon.

En effet, le document Java indique que les méthodes statiques peuvent faciliter l'organisation des méthodes d'assistance, par exemple lors de l'appel de méthodes d'assistance à partir de méthodes par défaut.

Markus Pscheidt
la source
1
Je ne pense pas que ce soit une réponse à la question posée, car vous pourriez faire ces deux choses sans cette nouvelle fonctionnalité.
Adrian
Ah, donc par la documentation, il s'agit d'une méthode d'aide. Cependant, il est également public, ce qui le contredit. Il est donc peut-être destiné à aider également les classes qui implémentent l'interface. Mais ils ne sont pas hérités, vous devez donc utiliser l'importation statique pour donner l'impression que vous en avez hérité. Eh bien, merci de le chercher dans les documents.
Vlasec
2
Il n'y a absolument rien de mal à mettre des constantes dans les interfaces, si ces constantes font en quelque sorte partie de l'API décrite par l'interface. La seule chose qui est un anti-modèle est de mettre des constantes dans des interfaces sans méthodes et de faire en sorte que les classes implémentent l'interface uniquement pour faire référence aux constantes sans préfixe de classe. C'est un abus d'interfaces et, depuis l'introduction des importations statiques, complètement obsolète.
Michael Borgwardt
1

Comme indiqué dans la question, l'interface n'est pas destinée à être implémentée (ou instanciée). Nous pourrions donc le comparer avec une classe qui a un constructeur privé:

  • La classe ne peut pas être étendue sans super()appel, mais l'interface peut être "implémentée"
  • La classe ne peut pas être instanciée sans réflexions, tandis que l'interface peut être facilement instanciée comme une classe anonyme aussi simple que ceci: new MyInterface() {};

Cette comparaison est en faveur de la classe car elle permet plus de contrôle. Cela dit, la classe n'est pas non plus destinée à contenir des éléments statiques, vous vous attendez à ce qu'elle contienne des instances. Eh bien, il n'y a pas d'option parfaite.

Il y a aussi une chose étrange à propos de l'héritage de "l'interface statique":

  • Une classe "d'implémentation" hérite des champs, mais n'hérite pas des méthodes statiques
  • Une interface d'extension n'hérite d'aucun membre statique
  • (Bien qu'une classe étendant une classe hérite de chaque membre non privé ... et comme elle ne peut pas être étendue par une interface, il y a moins de place pour la confusion)

Ces raisons peuvent être considérées comme un mauvais modèle et continuer à utiliser des classes statiques pour ces cas. Cependant, pour quelqu'un accro au sucre syntaxique, il peut être tentant même avec les inconvénients.

Vlasec
la source
... Quoi qu'il en soit, les conventions de codage d'une équipe peuvent couvrir ces problèmes. Pour moi, même si elle n'attache pas la main d'un programmeur aussi étroitement qu'une classe avec un constructeur privé, elle comporte toujours moins de risques que les setters, et les setters sont fréquemment utilisés.
Vlasec
0

Comme @MarkusPscheidt, cette idée est essentiellement une extension de l'anti-modèle Constant Interface . Cette approche a d'abord été largement utilisée pour permettre à un seul ensemble de constantes d'être hérité par de nombreuses classes disparates. La raison pour laquelle cela a fini par être considéré comme un contre-motif était due aux problèmes rencontrés lors de l'utilisation de cette approche dans des projets réels. Je ne vois aucune raison de penser que ces problèmes ne concerneraient que les constantes.

Plus important probablement, il n'y a vraiment pas besoin de le faire. Utilisez plutôt des importations statiques et soyez explicite sur ce que vous importez et d'où.

JimmyJames
la source
Oui, les importations statiques semblent être une meilleure idée que l'héritage de constantes et cela fonctionne avec les membres statiques des classes et des interfaces. Et il n'est visible qu'à l'intérieur de la classe / interface.
Vlasec
@Vlasec Oui, mais il n'est pas nécessaire de le faire sur une interface. La seule différence entre le faire avec une classe et une interface est que vous pouvez hériter de plusieurs interfaces. La pratique standard pour une classe utilitaire comme celle-ci est de rendre le constructeur privé pour empêcher l'instanciation ou l'extension. Il n'y a aucun avantage à faire cela dans une interface sur une classe. Cela ouvre simplement la porte à une mauvaise utilisation.
JimmyJames
Je peux voir la valeur des constantes sur les interfaces. Les interfaces ont tendance à définir des méthodes et les méthodes acceptent parfois des constantes comme paramètres. Par exemple: interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }. Je peux imaginer qu'il y ait des constantes différentes RGB, RGBA, CMYK, GREYSCALE, INDEXEDetc.
Stijn de Witt
@StijndeWitt Ce ne serait pas la pire des choses, mais vous pouvez utiliser une énumération ou une classe imbriquée sur l'interface à cet effet. Le vrai problème est d'utiliser cela pour introduire la statique dans la portée de nombreuses classes par héritage. Cette approche est nettement inférieure à l'utilisation d'importations statiques.
JimmyJames
0

Je dirais que si vous souhaitez utiliser correctement la nouvelle fonctionnalité, jetez un œil au code dans le JDK. Les méthodes par défaut sur les interfaces sont très largement utilisées et faciles à trouver, mais les méthodes statiques sur les interfaces semblent être très rares. En voici quelques exemples java.time.chrono.Chronology, java.util.Comparator, java.util.Map, et un certain nombre d' interfaces dans java.util.functionet java.util.stream.

La plupart des exemples que j'ai examinés impliquent des utilisations de ces API qui sont susceptibles d'être très courantes: conversion entre différents types dans l'API temporelle, par exemple, pour des comparaisons susceptibles d'être effectuées fréquemment entre des objets passés à des fonctions ou des objets contenus dans collections. Ma conclusion: s'il y a une partie de votre API qui doit être flexible, mais vous pouvez couvrir 80% des cas d'utilisation avec une poignée de valeurs par défaut, envisagez d'utiliser des méthodes statiques sur vos interfaces. Étant donné la fréquence à laquelle cette fonctionnalité semble être utilisée dans le JDK, je serais très prudent lorsque j'en ferais usage dans mon propre code.

Vos exemples ne ressemblent en rien à ce que je vois dans le JDK. Pour ces cas spécifiques, vous devriez presque certainement créer un enumqui implémente l'interface souhaitée. Une interface décrit un ensemble de comportements et n'a vraiment aucun intérêt à maintenir une collection de ses implémentations.

ngreen
la source
-1

Je me demande s'il y a des raisons de ne pas l'utiliser.

Que diriez-vous, euh, de la compatibilité avec les anciennes machines virtuelles Java?

Considérez-vous cela comme une bonne idée en général?

Non. C'est un changement incompatible vers l'arrière qui ajoute du zèle à l'expressivité de la langue. Deux pouces vers le bas.

Existe-t-il des situations modèles où vous recommandez fortement des cours?

Je recommande fortement d'utiliser des classes pour contenir les détails d'implémentation. Une interface est simplement destinée à dire "cet objet prend en charge les opérations XYZ" et cela n'a rien à voir avec les méthodes statiques que vous pourriez penser mettre dans la déclaration d'interface. Cette fonction est comme un fauteuil inclinable avec un mini-réfrigérateur intégré. Bien sûr, il a des utilisations de niche, mais il ne tire pas assez de poids pour mériter d'être intégré.

MISE À JOUR: S'il vous plaît plus de downvotes. De toute évidence, certaines personnes pensent que les méthodes statiques dans les interfaces sont les genoux de l'abeille, et je capitule par la présente. Je supprimerais tout de suite cette réponse, mais les commentaires qui y sont joints sont une discussion intéressante.

DépriméDaniel
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
maple_shaft
Si j'ai rétrogradé votre question, je le ferais parce que nos deux premières réponses ne sont pas de vraies réponses. La nouvelle version du langage apporte de nouveaux outils pour faciliter les choses, c'est le but même de ces nouvelles versions. La troisième réponse est un peu perdue dans les décombres des deux premiers brisés.
Vlasec