Classe imbriquée statique en Java, pourquoi?


217

Je regardais le code Java LinkedListet a remarqué qu'il a fait usage d'une classe imbriquée statique, Entry.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

Quelle est la raison d'utiliser une classe imbriquée statique, plutôt qu'une classe interne normale?

La seule raison pour laquelle je pouvais penser, c'est que Entry n'a pas accès aux variables d'instance, donc d'un point de vue POO, il a une meilleure encapsulation.

Mais je pensais qu'il pourrait y avoir d'autres raisons, peut-être la performance. Qu'est-ce que ça pourrait être?

Remarque. J'espère avoir mes termes corrects, je l'aurais appelé une classe interne statique, mais je pense que c'est faux: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html


Réponses:


271

La page Sun à laquelle vous créez un lien présente quelques différences clés entre les deux:

Une classe imbriquée est membre de sa classe englobante. Les classes imbriquées non statiques (classes internes) ont accès aux autres membres de la classe englobante, même si elles sont déclarées privées. Les classes imbriquées statiques n'ont pas accès aux autres membres de la classe englobante.
...

Remarque: Une classe imbriquée statique interagit avec les membres d'instance de sa classe externe (et d'autres classes) comme toute autre classe de niveau supérieur. En effet, une classe imbriquée statique est comportementalement une classe de niveau supérieur qui a été imbriquée dans une autre classe de niveau supérieur pour la commodité de l'empaquetage.

Il n'y a pas besoin d' LinkedList.Entryêtre classe de niveau supérieur car il est uniquement utilisé par LinkedList(il y a d'autres interfaces qui ont également des classes imbriquées statiques nommées Entry, telles que Map.Entry- même concept). Et comme il n'a pas besoin d'accéder aux membres de LinkedList, il est logique qu'il soit statique - c'est une approche beaucoup plus propre.

Comme le souligne Jon Skeet , je pense que c'est une meilleure idée si vous utilisez une classe imbriquée est de commencer par qu'elle soit statique, puis de décider si elle doit vraiment être non statique en fonction de votre utilisation.


Bah, je n'arrive pas à avoir un lien d'ancrage vers le commentaire pour travailler, mais c'est ce commentaire:#comment113712_253507
Zach Lysobey

1
@matt b Si une classe imbriquée statique n'a pas accès aux membres d'instance de la classe Outer, comment interagit-elle avec les membres d'instance de la classe Outer?
Geek

1
@mattb Mais comment @Geek l'a remarqué, la page Sun est contradictoire: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class comment est-ce possible si seulement un paragraphe avant que les docs disent que: Static nested classes do not have access to other members of the enclosing class Peut - être qu'ils aimeraient dire: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix

1
@DavidS Merci pour le lien! Ouais, j'avais tort, en lisant mon commentaire maintenant je vois que ma reformulation était incorrecte. Comme vous l'avez dit:, An inner class interacts with the instance members through an implicit reference to its enclosing classet cela souligne une autre propriété intéressante de non-static inner classesainsi que anonymous inner classesou local classes defined inside a block: ils ne peuvent pas tous avoir un no-argconstructeur car le compilateur ajoutera implicitement la séquence arg de chaque constructeur afin de passer une référence d'une instance de l'enveloppe classe. Assez simple.
tonix

1
vous pouvez utiliser une classe interne statique pour instancier la classe externe qui n'a qu'un constructeur privé. Ceci est utilisé dans le modèle de générateur. Vous ne pouvez pas faire de même avec la classe interne.
vuimurugan

47

À mon avis, la question devrait être l'inverse chaque fois que vous voyez une classe interne - doit-elle vraiment être une classe interne, avec la complexité supplémentaire et la référence implicite (plutôt qu'explicite et plus claire, IMO) à une instance de la classe contenante?

Attention, je suis biaisé en tant que fan de C # - C # n'a pas l'équivalent des classes internes, bien qu'il ait des types imbriqués. Je ne peux pas encore dire que j'ai manqué des cours internes :)


4
Je peux me tromper, mais cela me semble être un exemple de classe imbriquée statique, pas une classe interne. Ils spécifient même dans l'exemple qu'ils n'ont pas accès aux variables d'instance sur la classe environnante dans la classe imbriquée.
ColinD

Oui, Colin a raison - C # n'a pas de classes internes, il a des classes imbriquées. Attention, une classe imbriquée statique en C # n'est pas la même chose qu'une classe imbriquée statique en Java :)
Jon Skeet

2
Les types imbriqués sont l'un de ces domaines où C # est extrêmement correct par rapport à Java. Je m'émerveille toujours de sa justesse sémantique / logique ..
nawfal

3
@nawfal: Oui, sauf quelques soucis, je suis impressionné par la façon dont le langage C # a été conçu (et spécifié).
Jon Skeet

1
@ JonSkeet avez-vous un article ou un blog sur ce que sont ces niggles?
J'adorerais

27

Il y a des problèmes de rétention de mémoire non évidents à prendre en compte ici. Puisqu'une classe interne non statique maintient une référence implicite à sa classe «externe», si une instance de la classe interne est fortement référencée, alors l'instance externe est également fortement référencée. Cela peut entraîner des grattements de tête lorsque la classe externe n'est pas récupérée, même s'il semble que rien ne la référence.


Si la classe «externe» est finale et ne peut donc pas du tout être instanciée, cet argument a-t-il un sens dans ce cas? Car avoir / garder une référence à une classe externe est inutile, si celle-ci est définitive.
getsadzeg

10

Eh bien, pour une chose, les classes internes non statiques ont un champ caché supplémentaire qui pointe vers l'instance de la classe externe. Donc, si la classe Entry n'était pas statique, en plus d'avoir accès dont elle n'a pas besoin, elle porterait environ quatre pointeurs au lieu de trois.

En règle générale, je dirais que si vous définissez une classe qui est essentiellement là pour agir comme une collection de membres de données, comme une "structure" en C, envisagez de la rendre statique.


8

La classe interne statique est utilisée dans le modèle de générateur. La classe interne statique peut instancier sa classe externe qui n'a qu'un constructeur privé. Vous pouvez donc utiliser la classe interne statique pour instancier la classe externe qui n'a qu'un constructeur privé. Vous ne pouvez pas faire de même avec la classe interne car vous devez avoir un objet de la classe externe créé avant d'accéder à la classe interne.

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Cela produira x: 1


7

La classe imbriquée statique est comme toute autre classe externe, car elle n'a pas accès aux membres de la classe externe.

Juste pour la commodité de l'emballage, nous pouvons regrouper les classes imbriquées statiques en une classe externe à des fins de lisibilité. En dehors de cela, il n'y a pas d'autre cas d'utilisation de classe imbriquée statique.

Exemple pour ce type d'utilisation, vous pouvez le trouver dans le fichier Android R.java (ressources). Le dossier Res d'Android contient des mises en page (contenant des conceptions d'écran), un dossier dessinable (contenant des images utilisées pour le projet), un dossier de valeurs (qui contient des constantes de chaîne), etc.

Sine, tous les dossiers font partie du dossier Res, l'outil Android génère un fichier R.java (ressources) qui contient en interne beaucoup de classes imbriquées statiques pour chacun de leurs dossiers internes.

Voici l'aspect et la convivialité du fichier R.java généré dans Android: Ici, ils ne sont utilisés que pour la commodité de l'emballage.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}


4

Exemple simple:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Si non statique, la classe ne peut pas être instanciée sauf dans une instance de la classe supérieure (donc pas dans l'exemple où main est une fonction statique)


votre StaticInnerClass n'est pas, en fait, une classe statique imbriquée / interne. c'est une classe statique de haut niveau.
theRiley

2

L'une des raisons de la statique par rapport à la normale est liée au chargement de classe. Vous ne pouvez pas instancier une classe interne dans le constructeur de son parent.

PS: J'ai toujours compris que «imbriqué» et «intérieur» étaient interchangeables. Il peut y avoir de subtiles nuances dans les termes, mais la plupart des développeurs Java comprendraient l'un ou l'autre.


1

Les classes internes non statiques peuvent entraîner des fuites de mémoire tandis que la classe interne statique les protégera. Si la classe externe contient des données considérables, cela peut réduire les performances de l'application.


«intérieur statique» est une contradiction en termes.
Marquis de Lorne

1
@EJP, sheesh ... les gens s'en sortent vraiment en le signalant chaque fois que quelqu'un mentionne des "classes internes statiques" ...
Sakiboy

0

Je ne connais pas la différence de performances, mais comme vous le dites, la classe imbriquée statique ne fait pas partie d'une instance de la classe englobante. Il semble plus simple de créer une classe imbriquée statique, sauf si vous en avez vraiment besoin pour être une classe interne.

C'est un peu comme pourquoi je rend toujours mes variables finales en Java - si elles ne sont pas finales, je sais qu'il se passe quelque chose de drôle avec elles. Si vous utilisez une classe interne au lieu d'une classe imbriquée statique, il devrait y avoir une bonne raison.


Une classe interne ne fait pas non plus partie d'une instance de la classe englobante.
Marquis de Lorne

une classe interne dépend existentiellement de la classe englobante et a un accès intime aux membres de la classe englobante, elle fait donc partie de la classe englobante. en fait, c'est un membre.
theRiley

0

L'utilisation d'une classe imbriquée statique plutôt que non statique peut économiser de l'espace dans certains cas. Par exemple: implémenter un Comparatordans une classe, par exemple Étudiant.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Ensuite, le staticvérifie que la classe Student n'a qu'un seul comparateur, plutôt que d'en instancier un nouveau chaque fois qu'une nouvelle instance student est créée.


-1

Avantage de la classe interne -

  1. utilisation unique
  2. prend en charge et améliore l'encapsulation
  3. lisibilité
  4. accès au terrain privé

Sans existence de classe externe, la classe interne n'existera pas.

class car{
    class wheel{

    }
}

Il existe quatre types de classes internes.

  1. classe intérieure normale
  2. Méthode Classe interne locale
  3. Classe intérieure anonyme
  4. classe intérieure statique

point ---

  1. à partir de la classe interne statique, nous ne pouvons accéder qu'aux membres statiques de la classe externe.
  2. À l'intérieur de la classe interne, nous ne pouvons pas déclarer membre statique.
  3. afin d'invoquer la classe interne normale dans la zone statique de la classe externe.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. afin d'invoquer la classe interne normale dans la zone d'instance de la classe externe.

    Inner i=new Inner();

  5. afin d'invoquer la classe interne normale en dehors de la classe externe.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. inside Inner class Ce pointeur sur la classe interne.

    this.member-current inner class outerclassname.this--outer class

  7. pour le modificateur applicable à la classe interne est - public, par défaut,

    final,abstract,strictfp,+private,protected,static

  8. external $ inner est le nom du nom de la classe interne.

  9. classe interne à l'intérieur de la méthode d'instance, nous pouvons accéder au champ statique et d'instance de la classe externe.

10. classe intérieure à l'intérieur de la méthode statique, nous ne pouvons accéder qu'au champ statique de

classe externe.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.