t
t
t
t
t t   8) Interfaces et classes internes
tttt
t
carrea) Préface carreb) Avant-propos carre1) Introduction sur les &laqo; objets » carre2) Tout est &laqo; objet » carre3) Contrôle du flux du programme carre4) Initialization & Cleanup carre5) Cacher l'implémentation carre6) Réutiliser les classes carre7) Polymorphisme 8) Interfaces & classes internes carre9) Stockage des objets carre10) Error Handling with Exceptions carre11) Le système d’E/S de Java carre12) Identification dynamique de type carre13) Création de fenêtres & d'Applets carre14) Les &laqo; Threads » multiples carre15) Informatique distribuée carreA) Passage et retour d'objets carreB) L'Interface Java Natif (JNI) carreC) Conseils pour une programation stylée en Java carreD) Resources
Texte original t Traducteur : Jérome Quelin
t
t
///
Ce chapitre contient 6 pages
1 2 3 4 5 6
\\\
t t t
t t t
t
t t t
In the example, main( ) must be in a separate class in order to demonstrate the privateness of the inner class PContents.
t Dans cet exemple, main() doit être dans une classe séparée afin de démontrer le caractère private de la classe interne PContents.
t t t
In Parcel3, something new has been added: the inner class PContents is private so no one but Parcel3 can access it. PDestination is protected, so no one but Parcel3, classes in the Parcel3 package (since protected also gives package access—that is, protected is also “friendly”), and the inheritors of Parcel3 can access PDestination. This means that the client programmer has restricted knowledge and access to these members. In fact, you can’t even downcast to a private inner class (or a protected inner class unless you’re an inheritor), because you can’t access the name, as you can see in class Test. Thus, the private inner class provides a way for the class designer to completely prevent any type-coding dependencies and to completely hide details about implementation. In addition, extension of an interface is useless from the client programmer’s perspective since the client programmer cannot access any additional methods that aren’t part of the public interface class. This also provides an opportunity for the Java compiler to generate more efficient code.
t Dans Parcel3, de nouvelles particularités ont été ajoutées : la classe interne PContents est private afin que seule Parcel3 puisse y accéder. PDestination est protected, afin que seules Parcel3, les classes du packages de Parcel3 (puisque protected fournit aussi un accès package - c'est à dire que protected est « amical »), et les héritiers de Parcel3 puissent accéder à PDestination. Cela signifie que le programmeur client n'a qu'une connaissance et des accès restreints à ces membres. En fait, on ne peut faire de transtypage descendant vers une classe interne private (ou une classe interne protected à moins d'être un héritier), parce qu'on ne peut accéder à son nom, comme on peut le voir dans la classe Test. La classe interne private fournit donc un moyen pour le concepteur de la classe d'interdire tout code testant le type et de cacher complètement les détails de l'implémentation. De plus, l'extension d'une interface est inutile du point de vue du programmeur client puisqu'il ne peut accéder à aucune méthode additionnelle ne faisant pas partie de l'interface public de la classe. Cela permet aussi au compilateur Java de générer du code plus efficace.
t t t
Normal (non-inner) classes cannot be made private or protected—only public or “friendly.”
t Les classes normales (non internes) ne peuvent être déclarées private ou protected - uniquement public ou « amicales ».
t t t

Inner classes
in methods and scopes

t

Classes internes définies dans des méthodes et autres portées

t t t
What you’ve seen so far encompasses the typical use for inner classes. In general, the code that you’ll write and read involving inner classes will be “plain” inner classes that are simple and easy to understand. However, the design for inner classes is quite complete and there are a number of other, more obscure, ways that you can use them if you choose: inner classes can be created within a method or even an arbitrary scope. There are two reasons for doing this:
t Ce qu'on a pu voir jusqu'à présent constitue l'utilisation typique des classes internes. En général, le code impliquant des classes internes que vous serez amené à lire et à écrire ne mettra en oeuvre que des classes internes « régulières », et sera simple à comprendre. Cependant, le support des classes internes est relativement complet et il existe de nombreuses autres manières, plus obscures, de les utiliser si on le souhaite : les classes internes peuvent être créées à l'intérieur d'une méthode ou même d'une portée arbitraire. Deux raisons possibles à cela :
t t t
  1. As shown previously, you’re implementing an interface of some kind so that you can create and return a reference.
  2. You’re solving a complicated problem and you want to create a class to aid in your solution, but you don’t want it publicly available.
t
  1. Comme montré précédemment, on implémente une interface d'un certain type afin de pouvoir créer et renvoyer une référence.
  2. On résoud un problème compliqué pour lequel la création d'une classe aiderait grandement, mais on ne veut pas la rendre publiquement accessible.
t t t
In the following examples, the previous code will be modified to use:
t Dans les exemples suivants, le code précédent est modifié afin d'utiliser :
t t t
  1. A class defined within a method
  2. A class defined within a scope inside a method
  3. An anonymous class implementing an interface
  4. An anonymous class extending a class that has a nondefault constructor
  5. An anonymous class that performs field initialization
  6. An anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors)
t
  1. Une classe définie dans une méthode
  2. Une classe définie dans une portée à l'intérieur d'une méthode
  3. Une classe anonyme implémentant une interface
  4. Une classe anonyme étendant une classe qui dispose d'un constructeur autre que le constructeur par défaut
  5. Une classe anonyme réalisant des initialisations de champs
  6. Une classe anonyme qui se construit en initialisant des instances (les classes internes anonymes ne peuvent avoir de constructeurs)
t t t
Although it’s an ordinary class with an implementation, Wrapping is also being used as a common “interface” to its derived classes:
t Bien que ce soit une classe ordinaire avec une implémentation, Wrapping est aussi utilisée comme une « interface » commune pour ses classes dérivées :
t t t
//: c08:Wrapping.java public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///:~ t
//: c08:Wrapping.java
public class Wrapping {
  private int i;
  public Wrapping(int x) { i = x; }
  public int value() { return i; }
} ///:~
t t t
You’ll notice above that Wrapping has a constructor that requires an argument, to make things a bit more interesting.
t Notez que Wrapping dispose d'un constructeur requérant un argument, afin de rendre les choses un peu plus intéressantes.
t t t
The first example shows the creation of an entire class within the scope of a method (instead of the scope of another class):
t Le premier exemple montre la création d'une classe entière dans la portée d'une méthode (au lieu de la portée d'une autre classe) :
t t t
//: c08:Parcel4.java // Nesting a class within a method. public class Parcel4 { public Destination dest(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new PDestination(s); } public static void main(String[] args) { Parcel4 p = new Parcel4(); Destination d = p.dest("Tanzania"); } } ///:~ t
//: c08:Parcel4.java
// Définition d'une classe à l'intérieur d'une méthode.

public class Parcel4 {
  public Destination dest(String s) {
    class PDestination
        implements Destination {
      private String label;
      private PDestination(String whereTo) {
        label = whereTo;
      }
      public String readLabel() { return label; }
    }
    return new PDestination(s);
  }
  public static void main(String[] args) {
    Parcel4 p = new Parcel4();
    Destination d = p.dest("Tanzania");
  }
} ///:~
t t t
The class PDestination is part of dest( ) rather than being part of Parcel4. (Also notice that you could use the class identifier PDestination for an inner class inside each class in the same subdirectory without a name clash.) Therefore, PDestination cannot be accessed outside of dest( ). Notice the upcasting that occurs in the return statement—nothing comes out of dest( ) except a reference to Destination, the base class. Of course, the fact that the name of the class PDestination is placed inside dest( ) doesn’t mean that PDestination is not a valid object once dest( ) returns.
t La classe PDestination appartient à dest() plutôt qu'à Parcel4 (notez aussi qu'on peut utiliser l'identifiant PDestination pour une classe interne à l'intérieur de chaque classe du même sous-répertoire sans collision de nom). Cependant, PDestination ne peut être accédée en dehors de dest(). Notez le transtypage ascendant réalisé par l'instruction de retour - dest() ne renvoie qu'une référence à Destination, la classe de base. Bien sûr, le fait que le nom de la classe PDestination soit placé à l'intérieur de dest() ne veut pas dire que PDestination n'est pas un objet valide une fois sorti de dest().
t t t
The next example shows how you can nest an inner class within any arbitrary scope:
t L'exemple suivant montre comment on peut imbriquer une classe interne à l'intérieur de n'importe quelle portée :
t t t
//: c08:Parcel5.java // Nesting a class within a scope. public class Parcel5 { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } // Can't use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip("x"); } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel5 p = new Parcel5(); p.track(); } } ///:~ t
//: c08:Parcel5.java
// Définition d'une classe à l'intérieur d'une portée quelconque.

public class Parcel5 {
  private void internalTracking(boolean b) {
    if(b) {
      class TrackingSlip {
        private String id;
        TrackingSlip(String s) {
          id = s;
        }
        String getSlip() { return id; }
      }
      TrackingSlip ts = new TrackingSlip("slip");
      String s = ts.getSlip();
    }
    // Utilisation impossible ici ! En dehors de la portée :
    //! TrackingSlip ts = new TrackingSlip("x");
  }
  public void track() { internalTracking(true); }
  public static void main(String[] args) {
    Parcel5 p = new Parcel5();
    p.track();
  }
} ///:~
t t t
The class TrackingSlip is nested inside the scope of an if statement. This does not mean that the class is conditionally created—it gets compiled along with everything else. However, it’s not available outside the scope in which it is defined. Other than that, it looks just like an ordinary class.
t La classe TrackingSlip est définie dans la portée de l'instruction if. Cela ne veut pas dire que la classe est créée conditionnellement - elle est compilée avec tout le reste. Cependant, elle n'est pas accessible en dehors de la portée dans laquelle elle est définie. Mis à part cette restriction, elle ressemble à n'importe quelle autre classe ordinaire.
t t t

Anonymous inner classes

t

Classes internes anonymes

t t t
The next example looks a little strange:
t L'exemple suivant semble relativement bizarre :
t t t
//: c08:Parcel6.java // A method that returns an anonymous inner class. public class Parcel6 { public Contents cont() { return new Contents() { private int i = 11; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel6 p = new Parcel6(); Contents c = p.cont(); } } ///:~ t
//: c08:Parcel6.java
// Une méthode qui renvoie une classe interne anonyme.

public class Parcel6 {
  public Contents cont() {
    return new Contents() {
      private int i = 11;
      public int value() { return i; }
    }; // Point-virgule requis dans ce cas
  }
  public static void main(String[] args) {
    Parcel6 p = new Parcel6();
    Contents c = p.cont();
  }
} ///:~
t t t
The cont( ) method combines the creation of the return value with the definition of the class that represents that return value! In addition, the class is anonymous—it has no name. To make matters a bit worse, it looks like you’re starting out to create a Contents object:
t La méthode cont() combine la création d'une valeur de retour avec la définition de la classe de cette valeur de retour ! De plus, la classe est anonyme - elle n'a pas de nom. Pour compliquer le tout, il semble qu'on commence par créer un objet Contents :
t t t
return new Contents() t
return new Contents()
t t t
But then, before you get to the semicolon, you say, “But wait, I think I’ll slip in a class definition”:
t Mais alors, avant de terminer l'instruction par un point-virgule, on dit : « Eh, je crois que je vais insérer une définition de classe » :
t t t
return new Contents() { private int i = 11; public int value() { return i; } }; t
return new Contents() {

  private int i = 11;
  public int value() { return i; }
};
t t t
What this strange syntax means is: “Create an object of an anonymous class that’s inherited from Contents.” The reference returned by the new expression is automatically upcast to a Contents reference. The anonymous inner-class syntax is a shorthand for:
t Cette étrange syntaxe veut dire : « Crée un objet d'une classe anonyme dérivée de Contents ». La référence renvoyée par l'expression new est automatiquement transtypée vers une référence Contents. La syntaxe d'une classe interne anonyme est un raccourci pour :
t t t
class MyContents implements Contents { private int i = 11; public int value() { return i; } } return new MyContents(); t
class MyContents implements Contents {
  private int i = 11;
  public int value() { return i; }
}
return new MyContents();
t t t
In the anonymous inner class, Contents is created using a default constructor. The following code shows what to do if your base class needs a constructor with an argument:
t Dans la classe interne anonyme, Contents est créée avec un constructeur par défaut. Le code suivant montre ce qu'il faut faire dans le cas où la classe de base dispose d'un constructeur requérant un argument :
t t t
//: c08:Parcel7.java // An anonymous inner class that calls // the base-class constructor. public class Parcel7 { public Wrapping wrap(int x) { // Base constructor call: return new Wrapping(x) { public int value() { return super.value() * 47; } }; // Semicolon required } public static void main(String[] args) { Parcel7 p = new Parcel7(); Wrapping w = p.wrap(10); } } ///:~ t
//: c08:Parcel7.java
// Une classe interne anonyme qui appelle
// le constructeur de la classe de base.

public class Parcel7 {
  public Wrapping wrap(int x) {
    // Appel du constructeur de la classe de base :
    return new Wrapping(x) {
      public int value() {
        return super.value() * 47;
      }
    }; // Point-virgule requis
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Wrapping w = p.wrap(10);
  }
} ///:~
t t t
That is, you simply pass the appropriate argument to the base-class constructor, seen here as the x passed in new Wrapping(x). An anonymous class cannot have a constructor where you would normally call super( ).
t Autrement dit, on passe simplement l'argument approprié au constructeur de la classe de base, vu ici comme le x utilisé dans new Wrapping(x). Une classe anonyme ne peut avoir de constructeur dans lequel on appellerait normalement super().
t t t
In both of the previous examples, the semicolon doesn’t mark the end of the class body (as it does in C++). Instead, it marks the end of the expression that happens to contain the anonymous class. Thus, it’s identical to the use of the semicolon everywhere else.
t Dans les deux exemples précédents, le point-virgule ne marque pas la fin du corps de la classe (comme il le fait en C++). Il marque la fin de l'expression qui se trouve contenir la définition de la classe anonyme. Son utilisation est donc similaire à celle que l'on retrouve partout ailleurs.
t t t
What happens if you need to perform some kind of initialization for an object of an anonymous inner class? Since it’s anonymous, there’s no name to give the constructor—so you can’t have a constructor. You can, however, perform initialization at the point of definition of your fields:
t Que se passe-t-il lorsque certaines initialisations sont nécessaires pour un objet d'une classe interne anonyme ? Puisqu'elle est anonyme, on ne peut donner de nom au constructeur - et on ne peut donc avoir de constructeur. On peut néanmoins réaliser des initialisations lors de la définition des données membres :
t t t
//: c08:Parcel8.java // An anonymous inner class that performs // initialization. A briefer version // of Parcel5.java. public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Destination d = p.dest("Tanzania"); } } ///:~ t
//: c08:Parcel8.java
// Une classe interne anonyme qui réalise
// des initialisations. Une version plus courte
// de Parcel5.java.

public class Parcel8 {
  // L'argument doit être final pour être utilisé
  // la classe interne anonyme :
  public Destination dest(final String dest) {
    return new Destination() {
      private String label = dest;
      public String readLabel() { return label; }
    };
  }
  public static void main(String[] args) {
    Parcel8 p = new Parcel8();
    Destination d = p.dest("Tanzania");
  }
} ///:~
t t t
If you’re defining an anonymous inner class and want to use an object that’s defined outside the anonymous inner class, the compiler requires that the outside object be final. This is why the argument to dest( ) is final. If you forget, you’ll get a compile-time error message.
t Si on définit une classe interne anonyme et qu'on veut utiliser un objet défini en dehors de la classe interne anonyme, le compilateur requiert que l'objet extérieur soit final. C'est pourquoi l'argument de dest() est final. Si le mot-clef est omis, le compilateur générera un message d'erreur.
t t t
As long as you’re simply assigning a field, the above approach is fine. But what if you need to perform some constructor-like activity? With instance initialization, you can, in effect, create a constructor for an anonymous inner class:
t Tant qu'on se contente d'assigner un champ, l'approche précédente est suffisante. Mais comment faire si on a besoin de réaliser plusieurs actions comme un constructeur peut être amené à le faire ? Avec l'initialisation d'instances, on peut, dans la pratique, créer un constructeur pour une classe interne anonyme :
t t t
//: c08:Parcel9.java // Using "instance initialization" to perform // construction on an anonymous inner class. public class Parcel9 { public Destination dest(final String dest, final float price) { return new Destination() { private int cost; // Instance initialization for each object: { cost = Math.round(price); if(cost > 100) System.out.println("Over budget!"); } private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.dest("Tanzania", 101.395F); } } ///:~ t
//: c08:Parcel9.java
// Utilisation des « initialisations d'instances » pour
// réaliser la construction d'une classe interne anonyme.

public class Parcel9 {
  public Destination
  dest(final String dest, final float price) {
    return new Destination() {
      private int cost;
      // Initialisation d'instance pour chaque objet :
      {
        cost = Math.round(price);
        if(cost > 100)
          System.out.println("Over budget!");
      }
      private String label = dest;
      public String readLabel() { return label; }
    };
  }
  public static void main(String[] args) {
    Parcel9 p = new Parcel9();
    Destination d = p.dest("Tanzania", 101.395F);
  }
} ///:~
t t t
Inside the instance initializer you can see code that couldn’t be executed as part of a field initializer (that is, the if statement). So in effect, an instance initializer is the constructor for an anonymous inner class. Of course, it’s limited; you can’t overload instance initializers so you can have only one of these constructors.
t A l'intérieur de l'initialisateur d'instance on peut voir du code pouvant ne pas être exécuté comme partie d'un initialisateur de champ (l'instruction if). Dans la pratique, donc, un initialisateur d'instance est un constructeur pour une classe interne anonyme. Bien sûr, ce mécanisme est limité ; on ne peut surcharger les initialisateurs d'instance et donc on ne peut avoir qu'un seul de ces constructeurs.
t t t

The link to the outer class

t

Lien vers la classe externe

t t t
So far, it appears that inner classes are just a name-hiding and code-organization scheme, which is helpful but not totally compelling. However, there’s another twist. When you create an inner class, an object of that inner class has a link to the enclosing object that made it, and so it can access the members of that enclosing object—without any special qualifications. In addition, inner classes have access rights to all the elements in the enclosing class[40]. The following example demonstrates this:
t Jusqu'à présent, les classes internes apparaissent juste comme un mécanisme de camouflage de nom et d'organisation du code, ce qui est intéressant mais pas vraiment indispensable. Cependant, elles proposent un autre intérêt. Quand on crée une classe interne, un objet de cette classe interne possède un lien vers l'objet extérieur qui l'a créé, il peut donc accéder aux membres de cet objet externe - sans aucune qualification spéciale. De plus, les classes internes ont accès à tous les éléments de la classe externe  [40]. L'exemple suivant le démontre :
t t t
//: c08:Sequence.java // Holds a sequence of Objects. interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private Object[] obs; private int next = 0; public Sequence(int size) { obs = new Object[size]; } public void add(Object x) { if(next < obs.length) { obs[next] = x; next++; } } private class SSelector implements Selector { int i = 0; public boolean end() { return i == obs.length; } public Object current() { return obs[i]; } public void next() { if(i < obs.length) i++; } } public Selector getSelector() { return new SSelector(); } public static void main(String[] args) { Sequence s = new Sequence(10); for(int i = 0; i < 10; i++) s.add(Integer.toString(i)); Selector sl = s.getSelector(); while(!sl.end()) { System.out.println(sl.current()); sl.next(); } } } ///:~ t
//: c08:Sequence.java
// Contient une séquence d'Objects.

interface Selector {
  boolean end();
  Object current();
  void next();
}

public class Sequence {
  private Object[] obs;
  private int next = 0;
  public Sequence(int size) {
    obs = new Object[size];
  }
  public void add(Object x) {
    if(next < obs.length) {
      obs[next] = x;
      next++;
    }
  }
  private class SSelector implements Selector {
    int i = 0;
    public boolean end() {
      return i == obs.length;
    }
    public Object current() {
      return obs[i];
    }
    public void next() {
      if(i < obs.length) i++;
    }
  }
  public Selector getSelector() {
    return new SSelector();
  }
  public static void main(String[] args) {
    Sequence s = new Sequence(10);
    for(int i = 0; i < 10; i++)
      s.add(Integer.toString(i));
    Selector sl = s.getSelector();    
    while(!sl.end()) {
      System.out.println(sl.current());
      sl.next();
    }
  }
} ///:~
t t t
The Sequence is simply a fixed-sized array of Object with a class wrapped around it. You call add( ) to add a new Object to the end of the sequence (if there’s room left). To fetch each of the objects in a Sequence, there’s an interface called Selector, which allows you to see if you’re at the end( ), to look at the current( ) Object, and to move to the next( ) Object in the Sequence. Because Selector is an interface, many other classes can implement the interface in their own ways, and many methods can take the interface as an argument, in order to create generic code.
t La Sequence est simplement un tableau d'Objects de taille fixe paqueté dans une classe. On peut appeler add() pour ajouter un nouvel Object à la fin de la séquence (s'il reste de la place). Pour retrouver chacun des objets dans une Sequence, il existe une interface appelée Selector, qui permet de vérifier si on se trouve à la fin (end()), de récupérer l'Object courant (current()), et de se déplacer vers l'Object suivant (next()) dans la Sequence. Comme Selector est une interface, beaucoup d'autres classes peuvent implémenter l'interface comme elles le veulent, et de nombreuses méthodes peuvent prendre l'interface comme un argument, afin de créer du code générique.
t t t
Here, the SSelector is a private class that provides Selector functionality. In main( ), you can see the creation of a Sequence, followed by the addition of a number of String objects. Then, a Selector is produced with a call to getSelector( ) and this is used to move through the Sequence and select each item.
t Ici, SSelector est une classe private qui fournit les fonctionnalités de Selector. Dans main(), on peut voir la création d'une Sequence, suivie par l'addition d'un certain nombre d'objets String. Un Selector est alors produit grâce à un appel à getSelector() et celui-ci est alors utilisé pour se déplacer dans la Sequence et sélectionner chaque item.
t t t
At first, the creation of SSelector looks like just another inner class. But examine it closely. Note that each of the methods end( ), current( ), and next( ) refer to obs, which is a reference that isn’t part of SSelector, but is instead a private field in the enclosing class. However, the inner class can access methods and fields from the enclosing class as if they owned them. This turns out to be very convenient, as you can see in the above example.
t Au premier abord, SSelector ressemble à n'importe quelle autre classe interne. Mais regardez-la attentivement. Notez que chacune des méthodes end(), current() et next() utilisent obs, qui est une référence n'appartenant pas à SSelector, un champ private de la classe externe. Cependant, la classe interne peut accéder aux méthodes et aux champs de la classe externe comme si elle les possédait. Ceci est très pratique, comme on peut le voir dans cet exemple.
t t t
So an inner class has automatic access to the members of the enclosing class. How can this happen? The inner class must keep a reference to the particular object of the enclosing class that was responsible for creating it. Then when you refer to a member of the enclosing class, that (hidden) reference is used to select that member. Fortunately, the compiler takes care of all these details for you, but you can also understand now that an object of an inner class can be created only in association with an object of the enclosing class. Construction of the inner class object requires the reference to the object of the enclosing class, and the compiler will complain if it cannot access that reference. Most of the time this occurs without any intervention on the part of the programmer.
t Une classe interne a donc automatiquement accès aux membres de la classe externe. Comment cela est-il possible ? La classe interne doit garder une référence de l'objet de la classe externe responsable de sa création. Et quand on accède à un membre de la classe externe, cette référence (cachée) est utilisée pour sélectionner ce membre. Heureusement, le compilateur gère tous ces détails pour nous, mais vous pouvez maintenant comprendre qu'un objet d'une classe interne ne peut être créé qu'en association avec un objet de la classe externe. La construction d'un objet d'une classe interne requiert une référence sur l'objet de la classe externe, et le compilateur se plaindra s'il ne peut accéder à cette référence. La plupart du temps cela se fait sans aucune intervention de la part du programmeur.
t t t

static inner classes

t

Classes internes static

t t t
If you don’t need a connection between the inner class object and the outer class object, then you can make the inner class static. To understand the meaning of static when applied to inner classes, you must remember that the object of an ordinary inner class implicitly keeps a reference to the object of the enclosing class that created it. This is not true, however, when you say an inner class is static. A static inner class means:
t Si on n'a pas besoin du lien entre l'objet de la classe interne et l'objet de la classe externe, on peut rendre la classe interne static. Pour comprendre le sens de static quand il est appliqué aux classes internes, il faut se rappeler que l'objet d'une classe interne ordinaire garde implicitement une référence sur l'objet externe qui l'a créé. Ceci n'est pas vrai cependant lorsque la classe interne est static. Une classe interne static implique que :
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel