 |
 |
8) Interfaces et classes internes |
|
 |
|
Texte original |
 |
Traducteur : Jérome QUELIN |
|
 |
///
|
Ce chapitre contient 6 pages
1
2
3
4
5
6
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
In the example, main( ) must be in a separate class in order to demonstrate the privateness of the inner class PContents.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Normal (non-inner) classes cannot be made private or protected—only public or “friendly.”
|
 |
Les classes normales (non internes) ne peuvent être déclarées
private ou protected - uniquement public ou
« amicales ».
|
 |
 |
 |
Inner classes in methods and scopes
|
 |
Classes internes définies dans des méthodes et autres portées
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
- As shown previously,
you’re implementing an interface of some kind so that you can create and return a reference. - 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.
|
 |
- 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.
- 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.
|
 |
 |
 |
In the following examples, the previous code will be modified to use:
|
 |
Dans les exemples suivants, le code précédent est modifié afin d'utiliser
:
|
 |
 |
 |
- A class defined within a
method - A class
defined within a scope inside a method - An anonymous
class implementing an interface - An
anonymous class extending a class that has a nondefault constructor - An
anonymous class that performs field initialization - An
anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors)
|
 |
- Une classe définie dans une méthode
- Une classe définie dans une portée à l'intérieur d'une méthode
- Une classe anonyme implémentant une interface
- Une classe anonyme étendant une classe qui dispose d'un constructeur autre
que le constructeur par défaut
- Une classe anonyme réalisant des initialisations de champs
- Une classe anonyme qui se construit en initialisant des instances (les
classes internes anonymes ne peuvent avoir de constructeurs)
|
 |
 |
 |
Although it’s an ordinary class with an implementation, Wrapping is also being used as a common “interface” to its derived classes:
|
 |
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 :
|
 |
 |
 |
//: c08:Wrapping.java public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///:~
|
 |
//: c08:Wrapping.java public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } ///:~
|
 |
 |
 |
You’ll notice above that
Wrapping has a constructor that requires an argument, to make things a
bit more interesting.
|
 |
Notez que Wrapping dispose d'un constructeur requérant un
argument, afin de rendre les choses un peu plus intéressantes.
|
 |
 |
 |
The first example shows the creation of
an entire class within the scope of a method (instead of the scope of another
class):
|
 |
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) :
|
 |
 |
 |
//: 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"); } } ///:~
|
 |
//: 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"); } } ///:~
|
 |
 |
 |
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.
|
 |
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().
|
 |
 |
 |
The next example shows how you can nest
an inner
class within any arbitrary scope:
|
 |
L'exemple suivant montre comment on peut imbriquer une classe interne
à l'intérieur de n'importe quelle portée :
|
 |
 |
 |
//: 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(); } } ///:~
|
 |
//: 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(); } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Anonymous inner classes
|
 |
Classes internes anonymes
|
 |
 |
 |
The next example looks a little
strange:
|
 |
L'exemple suivant semble relativement bizarre :
|
 |
 |
 |
//: 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(); } } ///:~
|
 |
//: 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(); } } ///:~
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
return new Contents()
|
 |
return new Contents()
|
 |
 |
 |
But then, before you get to the semicolon, you say, “But wait, I think I’ll slip in a class definition”:
|
 |
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 » :
|
 |
 |
 |
return new Contents() { private int i = 11; public int value() { return i; } };
|
 |
return new Contents() {
private int i = 11; public int value() { return i; } };
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
class MyContents implements Contents { private int i = 11; public int value() { return i; } } return new MyContents();
|
 |
class MyContents implements Contents { private int i = 11; public int value() { return i; } } return new MyContents();
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
//: 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); } } ///:~
|
 |
//: 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); } } ///:~
|
 |
 |
 |
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( ).
|
 |
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().
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
//: 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"); } } ///:~
|
 |
//: 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"); } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
//: 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); } } ///:~
|
 |
//: 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); } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
The link to the outer class
|
 |
Lien vers la classe externe
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
//: 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(); } } } ///:~
|
 |
//: 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(); } } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
static inner
classes
|
 |
Classes internes static
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |