web-dev-qa-db-fra.com

Pourquoi utiliser une interface imbriquée statique en Java?

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 retirais le static? Pourquoi quelqu'un ferait-il cela?

231
Mo.

Le mot clé static dans l'exemple ci-dessus est redondant (une interface imbriquée est automatiquement "statique") et peut être supprimé sans incidence 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 ne font qu'ajouter du fouillis 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 paquet-privé!)

C'est un style acceptable de créer une interface imbriquée de cette façon si vous prévoyez qu'elle ne sera utilisée qu'à partir de la classe externe, afin de ne pas créer un 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() {...}
});
291
Jesse Glick

La question a été résolue, mais l'une des bonnes raisons 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 un Listener. Si vous aviez une classe Foo et que vous vouliez que d'autres classes puissent écouter les événements, vous pouvez déclarer une interface nommée FooListener, ce qui est correct, mais ce serait probablement plus clair. pour déclarer une interface imbriquée et que ces autres classes implémentent Foo.Listener (une classe imbriquée Foo.Event _ n'est pas mauvais avec ça).

71
ColinD

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

14
Bas Leijdekkers

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, elle serait donc accessible avec Foo.Bar, ainsi:

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

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

9

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 à lire. Pouvez-vous trouver pourquoi l'interface interne est utile? La réponse est que la classe DoSomethingAlready peut être instanciée avec tout classe qui implémente A et C; pas seulement le zoo concret de classe. Bien sûr, cela peut être réalisé même si AC n’est pas interne, mais imaginez concaténer des noms plus longs (pas seulement A et C), et cela pour d’autres combinaisons (par exemple, A et B, C et B, etc.) et vous facilement. voyez comment les choses deviennent incontrôlables. Sans parler du fait que les personnes qui consultent votre arborescence de sources seront submergées par des interfaces qui n'ont de sens que dans une classe. Pour résumer, 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;
  }
}
6
user1982892

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

Map.Entry

aussi cela peut être utile

Entrée de blog statique Inerfaces imbriquées

3
Henry B

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

Autant que je sache, la seule différence en rendant une interface non statique est qu’elle peut désormais inclure des classes internes non statiques; la modification ne rend donc pas invalide les programmes existants Java.

Par exemple, il a proposé une solution au problème d’expression , c’est-à-dire le déséquilibre entre expression comme "combien votre langue peut-elle exprimer" 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 n’a jamais abouti à Java 1.5.0. Par conséquent, toutes les autres réponses sont correctes: il n’existe aucune différence entre les interfaces imbriquées statiques et non statiques.

0
Pindatjuh

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

interface définie dans une autre interface implicitement public static.

0
Danylo Volokh

En général, je vois des classes internes statiques. Les classes internes statiques ne peuvent pas référencer les classes contenant ce qui est le cas pour les classes non statiques. À moins que vous ne rencontriez des problèmes de collision de paquet (il y a déjà une interface appelée Bar dans le même paquet que Foo), je pense que j'en ferais un fichier propre. Il pourrait également être une décision de conception d'imposer la connexion logique entre Foo et Bar. Peut-être que l'auteur avait prévu que Bar ne soit utilisé qu'avec Foo (bien qu'une interface interne statique ne l'applique pas, mais simplement une connexion logique)

0
basszero