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 le sont.
Un exemple simple est juste une enveloppe pour les constantes globales. Par rapport à une classe, vous pouvez facilement remarquer le passe-partout manquant de public static final
comme ceux-ci sont supposés (ce qui le rend 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 soient pas hérités est un concept intéressant qui a été introduit uniquement dans 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.
Un exemple simple est juste une enveloppe pour les constantes globales.
Mettre des constantes sur les interfaces s'appelle le Constant Interface Antipattern
car les constantes ne sont souvent qu'un détail d'implémentation. D'autres inconvénients sont résumés dans l'article de Wikipedia sur le interface constante .
Vous pouvez également créer une "classe" utilitaire de cette façon.
En effet, le Java doc 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.
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é:
super()
à appeler, mais l'interface peut être "implémentée"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":
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.
Comme @MarkusPscheidt, cette idée est essentiellement une extension de l'anti-modèle Constant Interface . Cette approche a été initialement 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 importations statiques à la place et soyez explicite sur ce que vous importez et d'où.
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. Quelques exemples: Java.time.chrono.Chronology
, Java.util.Comparator
, Java.util.Map
, et pas mal d'interfaces dans Java.util.function
et 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 enum
qui 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.