Pourquoi une interface imbriquée statique serait-elle utilisée en Java?

235

Je viens de trouver une interface imbriquée statique dans notre base de code.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Je n'avais jamais vu ça avant. Le développeur d'origine est hors de portée. Je dois donc demander à SO:

Quelle est la sémantique derrière une interface statique? Qu'est-ce qui changerait si je supprimais le static? Pourquoi est-ce que quelqu'un ferait ça?

Mo.
la source
2
Ce n'est pas une interface interne: c'est une interface imbriquée . Inner a une signification spécifique en Java.
Marquis de Lorne

Réponses:

293

Le mot-clé statique dans l'exemple ci-dessus est redondant (une interface imbriquée est automatiquement "statique") et peut être supprimé sans aucun effet sur la sémantique; Je recommanderais qu'il soit supprimé. Il en va de même pour «public» sur les méthodes d'interface et «public final» sur les champs d'interface - les modificateurs sont redondants et ajoutent simplement de l'encombrement au code source.

Dans les deux cas, le développeur déclare simplement une interface nommée Foo.Bar. Il n'y a pas d'autre association avec la classe englobante, sauf que le code qui ne peut pas accéder à Foo ne pourra pas non plus accéder à Foo.Bar. (À partir du code source - le bytecode ou la réflexion peut accéder à Foo.Bar même si Foo est privé du package!)

Il est acceptable de créer une interface imbriquée de cette façon si vous vous attendez à ce qu'elle soit utilisée uniquement à partir de la classe externe, afin de ne pas créer de nouveau nom de niveau supérieur. Par exemple:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Jesse Glick
la source
1
Dans la réponse de Jesse Glick, ce que cela signifie: (à partir du code source - le bytecode ou la réflexion peut accéder à Foo.Bar même si Foo est privé de package!).
Vasu
Kaillash, les méthodes privées sont accessibles par la relection (dans le package de réflexion) et par l'accès direct au bytecode des fichiers .class générés.
gmoore
2
Par "bytecode ... peut accéder à Foo.Bar", je veux dire qu'une classe compilée référençant Foo.Bar peut être chargée et exécutée même si elle ne pouvait pas référencer Foo. Cela peut se produire si la classe a été compilée à un moment antérieur lorsque Foo était public, ou si la classe a été assemblée à la main ou compilée à partir d'un langage non Java, etc. Le compilateur Java, cependant, vérifie le modificateur d'accès sur la classe englobante même lorsque le bytecode résultant ne fait pas référence à cette classe englobante.
Jesse Glick
@Jesse Peut-on accéder à une classe statique privée dans une classe privée de niveau supérieur par le biais de la réflexion?
Pacerier
1
@Pacerier: non. Plus précisément, vous pouvez charger la classe et inspecter ses membres, mais vous ne pouvez pas l'instancier ou appeler des méthodes dessus sans utiliser setAccessible (true). En d'autres termes, il est visible, mais non accessible, via la réflexion. Mais si la classe statique imbriquée est publique, elle est accessible par défaut via la réflexion même si elle n'est pas accessible statiquement (lors de la compilation).
Jesse Glick
72

On a répondu à la question, mais une bonne raison d'utiliser une interface imbriquée est si sa fonction est directement liée à la classe dans laquelle elle se trouve. Un bon exemple de ceci est a Listener. Si vous aviez une classe Fooet que vous vouliez que d'autres classes puissent y écouter des événements, vous pourriez déclarer une interface nommée FooListener, ce qui est correct, mais il serait probablement plus clair de déclarer une interface imbriquée et de faire implémenter ces autres classes Foo.Listener( une classe imbriquée Foo.Eventn'est pas mauvaise avec cela).

ColinD
la source
11
Un exemple typique est java.util.Map.Entry(qui est une interface imbriquée dans une autre interface).
Paŭlo Ebermann
4
Je sais que c'est un vieux sujet, mais je préférerais que la classe externe soit dans son propre package et que toutes les interfaces supplémentaires (par exemple Map.Entry) ou classes soient également dans ce package. Je dis cela parce que j'aime garder mes cours courts et précis. Un lecteur peut également voir quelles autres entités sont liées à une classe en regardant les classes dans le package. J'aurais probablement un paquet java.collections.mapde cartes. Il s'agit d'OO et de modularité. java.utila trop en elle. utilc'est comme common- une odeur OMI
David Kerr
1
@Shaggy: a java.utilcertainement trop en elle. Cela dit, je ne pense pas non plus que le décomposer en paquets aussi fins que vous le suggérez soit idéal.
ColinD
1
@DavidKerr Supposons que vous appeliez cette interface en java.util.MapEntrydehors de son propre package. Premier réflexe: quelles sont les interfaces liées à cette classe? Je regarde dans la classe. À moins que javadoc ne lie à cette interface, je n'ai aucune idée de leur relation. De plus, les gens ne regardent pas le package d'importation. Chacun a ses propres opinions. Personne n'a raison, personne n'a tort
Raymond Chenon
@RaymondChenon partie de ce que j'ai dit était de mettre Map et ses classes associées, par exemple Map.Entry dans un package séparé, par exemple java.collections.map. De cette façon, tout le code lié à la carte est dans un seul emplacement (le package) et la classe Map de niveau supérieur n'est pas chargée. Je mettrais peut-être les implémentations associées (HashMap, etc.) dans le même package.
David Kerr
14

Les interfaces membres sont implicitement statiques. Le modificateur statique de votre exemple peut être supprimé sans modifier la sémantique du code. Voir aussi la spécification du langage Java 8.5.1. Déclarations de type de membre statique

Bas Leijdekkers
la source
Ce n'est pas une «interface interne»: c'est une interface imbriquée. Inner a une signification spécifique en Java.
Marquis de Lorne
@EJP, j'ai lu divers blogs en utilisant les termes de manière interchangeable. Existe-t-il une interface interne? En quoi sont-ils différents de l'interface imbriquée?
Number945
@BreakingBenjamin, interface imbriquée signifie statique. Il existe une classe interne et une classe imbriquée, mais pas d'interface interne. Même si c'est le cas, il doit être appelé en tant qu'interface imbriquée. Inner - non statique et imbriqué est statique.
Sundar Rajan
9

Une interface interne doit être statique pour être accessible. L'interface n'est pas associée aux instances de la classe, mais à la classe elle-même, donc elle serait accessible avec Foo.Bar, comme ceci:

public class Baz implements Foo.Bar {
   ...
}

Dans la plupart des cas, cela n'est pas différent d'une classe interne statique.

Clinton N. Dreisbach
la source
35
Une interface imbriquée est automatiquement statique, que l'on écrive ou non le mot-clé.
Paŭlo Ebermann
3
Nous avons vraiment besoin d'un moyen pour que la communauté vote pour accepter une réponse différente: stackoverflow.com/a/74400/632951
Pacerier
@ ClintonN.Dreisbach Pouvez-vous expliquer ce que l'on entend par The interface isn't associated with instances of the class, but with the class itselfplus loin, je ne l'ai pas compris
Kasun Siyambalapitiya
6

La réponse de Jesse est proche, mais je pense qu'il existe un meilleur code pour démontrer pourquoi une interface interne peut être utile. Regardez le code ci-dessous avant de continuer. Pouvez-vous trouver pourquoi l'interface interne est utile? La réponse est que la classe DoSomethingAlready peut être instanciée avec n'importe quelle classe qui implémente A et C; pas seulement le zoo de la classe concrète. Bien sûr, cela peut être réalisé même si AC n'est pas intérieur, mais imaginez concaténer des noms plus longs (pas seulement A et C), et le faire pour d'autres combinaisons (disons, A et B, C et B, etc.) et vous facilement voir comment les choses deviennent hors de contrôle. Sans oublier que les personnes qui examinent votre arbre source seront submergées par des interfaces qui n'ont de sens que dans une seule classe.une interface interne permet la construction de types personnalisés et améliore leur encapsulation .

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
user1982892
la source
2
Ça n'a pas de sens. La classe Zoon'implémente pas l'interface AC, par conséquent, les instances de Zoone peuvent pas être passées au constructeur DoSomethingAlreadydont elles attendent AC. Le fait que cela ACs'étende à la fois, Aet C, n'implique pas que les classes implémentant Aet Cimplémentent aussi par magie AC.
Holger
3

Pour répondre très directement à votre question, consultez Map.Entry.

Map.Entry

cela peut aussi être utile

Entrée de blog Inerfaces imbriquées statiques

Henry B
la source
4
Ceci est un exemple, mais pas vraiment une réponse.
Paŭlo Ebermann
J'utilise Map.Entry pour créer beaucoup d'objets "Pair". C'est exposé. L'implémentation de Pair a deux écoles de pensée, mais ce n'est pas le point ici. Map.Entry peut être intérieur mais je l'utilise à l'extérieur.
Ravindranath Akila
0

En général, je vois des classes internes statiques. Les classes internes statiques ne peuvent pas référencer les classes contenant, contrairement aux classes non statiques. À moins que vous ne rencontriez des collisions de packages (il existe déjà une interface appelée Bar dans le même package que Foo), je pense que je ferais de son propre fichier. Cela pourrait également être une décision de conception d'imposer la connexion logique entre Foo et Bar. L'auteur a peut-être voulu que Bar ne soit utilisé qu'avec Foo (bien qu'une interface interne statique ne l'impose pas, juste une connexion logique)

basszero
la source
«Intérieur statique» est une contradiction dans les termes. Les classes imbriquées sont soit statiques ou intérieure.
Marquis de Lorne
0

Si vous changez la classe Foo en interface Foo, le mot-clé "public" dans l'exemple ci-dessus sera également redondant car

L'interface définie dans une autre interface sera implicitement publique statique.

Danylo Volokh
la source
N'a pas répondu à la question
pleut le
0

En 1998, Philip Wadler a suggéré une différence entre les interfaces statiques et les interfaces non statiques.

Pour autant que je puisse voir, la seule différence pour rendre une interface non statique est qu'elle peut désormais inclure des classes internes non statiques; de sorte que la modification ne rendrait invalides aucun programme Java existant.

Par exemple, il a proposé une solution au problème de l' expression , qui est l'inadéquation entre l'expression comme «combien votre langue peut-elle exprimer» d'une part et l'expression comme «les termes que vous essayez de représenter dans votre langue» d'autre part .

Un exemple de la différence entre les interfaces imbriquées statiques et non statiques peut être vu dans son exemple de code :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Sa suggestion ne l'a jamais fait en Java 1.5.0. Par conséquent, toutes les autres réponses sont correctes: il n'y a aucune différence entre les interfaces imbriquées statiques et non statiques.

Pindatjuh
la source
Il convient de noter que tout cela se réfère à GJ qui était un processeur très précoce pour les génériques en Java.
Marquis de Lorne
-1

En Java, l'interface / classe statique permet à l'interface / classe d'être utilisée comme une classe de niveau supérieur, c'est-à-dire qu'elle peut être déclarée par d'autres classes. Vous pouvez donc faire:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Sans la statique, ce qui précède ne pourrait pas être compilé. L'avantage est que vous n'avez pas besoin d'un nouveau fichier source juste pour déclarer l'interface. Il associe également visuellement l'interface Bar à la classe Foo puisque vous devez écrire Foo.Bar et implique que la classe Foo fait quelque chose avec les instances de Foo.Bar.

Une description des types de classe en Java .

Skizz
la source
Sans la statique , il fait la compilation. Le «statique» est redondant.
Marquis de Lorne
@EJP: Selon cet article auquel je suis lié, sans la statique, la classe interne ne peut être utilisée que par la classe propriétaire et non par quelque chose en dehors de la classe due. La statique en fait une classe de niveau supérieur imbriquée et peut être utilisée par des objets en dehors du champ d'application de la classe propriétaire (au moins, selon l'article). La statique n'est pas entièrement redondante et affecte la portée de la classe interne. Cela s'applique aux classes, les interfaces peuvent toujours agir comme des objets de niveau supérieur (besoin de lire les spécifications du langage pour vérifier). Peut-être que j'aurais dû séparer les parties interface et classe de ma réponse (ma mauvaise).
Skizz
-6

Statique signifie que n'importe quelle partie de classe du package (projet) peut y accéder sans utiliser de pointeur. Cela peut être utile ou gênant selon la situation.

L'exemple parfait de l'utilité des méthodes "statiques" est la classe Math. Toutes les méthodes en mathématiques sont statiques. Cela signifie que vous n'avez pas à vous déplacer, créer une nouvelle instance, déclarer des variables et les stocker dans encore plus de variables, vous pouvez simplement entrer vos données et obtenir un résultat.

La statique n'est pas toujours aussi utile. Si vous effectuez une comparaison de cas par exemple, vous souhaiterez peut-être stocker des données de plusieurs manières différentes. Vous ne pouvez pas créer trois méthodes statiques avec des signatures identiques. Vous avez besoin de 3 instances différentes, non statiques, puis vous pouvez comparer, car si elles sont statiques, les données ne changeront pas avec l'entrée.

Les méthodes statiques sont bonnes pour les retours ponctuels et les calculs rapides ou les données faciles à obtenir.

Vordreller
la source
1
Je sais ce que signifie statique. Je ne l'ai jamais vu auparavant sur une interface. Par conséquent, votre question est hors sujet - désolé
lun
1
Je pense que sa réponse est hors sujet.
Koray Tugay