 |
 |
8) Interfaces et classes internes |
|
 |
|
Texte original |
 |
Traducteur :
Jérome Quelin |
|
 |
///
|
Ce chapitre contient
6 pages
1
2
3
4
5
6
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
- You don’t need an
outer-class object in order to create an object of a static inner
class.
- You
can’t access an outer-class object from an object of a static inner
class.
|
 |
- On n'a pas besoin d'un objet de la classe externe afin de créer un objet
de la classe interne static.
- On ne peut accéder à un objet de la classe externe depuis un objet de la
classe interne static.
|
 |
 |
 |
static inner
classes are different than non-static inner classes in another way, as
well. Fields and methods in non-static inner classes can only be at the
outer level of a class, so non-static inner classes cannot have
static data, static fields, or static inner classes.
However, static inner classes can have all of these:
|
 |
Les classes internes static diffèrent aussi des classes
internes non static d'une autre manière. Les champs et les méthodes des classes
internes non static ne peuvent être qu'au niveau externe de la classe, les classes
internes non static ne peuvent donc avoir de données static, de
champs static ou de classes internes static. Par contre, les
classes internes static peuvent avoir tout cela :
|
 |
 |
 |
//: c08:Parcel10.java
// Static inner classes.
public class Parcel10 {
private static class PContents
implements Contents {
private int i = 11;
public int value() { return i; }
}
protected static class PDestination
implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
// Static inner classes can contain
// other static elements:
public static void f() {}
static int x = 10;
static class AnotherLevel {
public static void f() {}
static int x = 10;
}
}
public static Destination dest(String s) {
return new PDestination(s);
}
public static Contents cont() {
return new PContents();
}
public static void main(String[] args) {
Contents c = cont();
Destination d = dest("Tanzania");
}
} ///:~
|
 |
//: c08:Parcel10.java // Classes internes static.
public class Parcel10 { private static class PContents implements Contents { private int i = 11; public int value() { return i; } } protected static class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } // Les classes internes static peuvent // contenir d'autres éléments static : public static void f() {} static int x = 10; static class AnotherLevel { public static void f() {} static int x = 10; } } public static Destination dest(String s) { return new PDestination(s); } public static Contents cont() { return new PContents(); } public static void main(String[] args) { Contents c = cont(); Destination d = dest("Tanzania"); } } ///:~
|
 |
 |
 |
In main( ), no object of
Parcel10 is necessary; instead you use the normal syntax for selecting a
static member to call the methods that return references to
Contents and Destination.
|
 |
Dans main(), aucun objet Parcel10 n'est
nécessaire ; on utilise à la place la syntaxe habituelle pour sélectionner un membre
static pour appeler les méthodes qui renvoient des références sur
Contents et Destination.
|
 |
 |
 |
As you will see shortly, in an ordinary
(non-static) inner class, the link to the outer class object is achieved
with a special this reference. A static inner class does not have
this special this reference, which makes it analogous to a static
method.
|
 |
Comme on va le voir bientôt, dans une classe interne ordinaire (non
static), le lien avec la classe externe est réalisé avec une référence spéciale
this. Une classe interne static ne dispose pas de cette référence
spéciale this, ce qui la rend analogue à une méthode
static.
|
 |
 |
 |
Normally you can’t put any code
inside an interface, but a static inner class can be part of an
interface. Since the class is static it doesn’t violate the
rules for interfaces—the static inner class is only placed inside
the namespace of the interface:
|
 |
Normalement, on ne peut placer du code à l'intérieur d'une
interface, mais une classe interne static peut faire partie d'une
interface. Comme la classe est static, cela ne viole pas les
règles des interfaces - la classe interne static est simplement placée dans
l'espace de noms de l'interface :
|
 |
 |
 |
//: c08:IInterface.java
// Static inner classes inside interfaces.
interface IInterface {
static class Inner {
int i, j, k;
public Inner() {}
void f() {}
}
} ///:~
|
 |
//: c08:IInterface.java // Classes internes static à l'intérieur d'interfaces.
interface IInterface { static class Inner { int i, j, k; public Inner() {} void f() {} } } ///:~
|
 |
 |
 |
Earlier in this book I suggested putting
a main( ) in every class to act as a test bed
for that class. One drawback to this is the amount of extra compiled code you
must carry around. If this is a problem, you can use a static inner class
to hold your test code:
|
 |
Plus tôt dans ce livre je suggérais de placer un main()
dans chaque classe se comportant comme un environnement de tests pour cette classe. Un
inconvénient de cette approche est le volume supplémentaire de code compilé qu'on doit supporter.
Si cela constitue un problème, on peut utiliser une classe interne static destinée
à contenir le code de test :
|
 |
 |
 |
//: c08:TestBed.java
// Putting test code in a static inner class.
class TestBed {
TestBed() {}
void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) {
TestBed t = new TestBed();
t.f();
}
}
} ///:~
|
 |
//: c08:TestBed.java // Code de test placé dans une classe interne static.
class TestBed { TestBed() {} void f() { System.out.println("f()"); } public static class Tester { public static void main(String[] args) { TestBed t = new TestBed(); t.f(); } } } ///:~
|
 |
 |
 |
This generates a separate class called
TestBed$Tester (to run the program, you say java TestBed$Tester).
You can use this class for testing, but you don’t need to include it in
your shipping product.
|
 |
Ceci génère une classe séparée appelée TestBed$Tester
(pour lancer le programme, il faut utiliser la commande java TestBed$Tester). On
peut utiliser cette classe lors des tests, mais on n'a pas besoin de l'inclure dans le produit
final.
|
 |
 |
 |
Referring to the outer class
object
|
 |
Se référer à l'objet de la classe externe
|
 |
 |
 |
If you need to produce the reference to
the outer class object, you name the outer class followed by a dot and
this. For example, in the class Sequence.SSelector, any of its
methods can produce the stored reference to the outer class Sequence by
saying Sequence.this. The resulting reference is automatically the
correct type. (This is known and checked at compile-time, so there is no
run-time overhead.)
|
 |
Si on a besoin de produire la référence à l'objet de la classe externe, il
faut utiliser le nom de la classe externe suivi par un point et this. Par exemple,
dans la classe Sequence.SSelector, chacune des méthodes peut accéder à la
référence à la classe externe Sequence stockée en utilisant
Sequence.this. Le type de la référence obtenue est automatiquement correct (il est
connu et vérifié lors de la compilation, il n'y a donc aucune pénalité sur les performances lors de
l'exécution).
|
 |
 |
 |
Sometimes you want to tell some other
object to create an object of one of its inner classes. To do this you must
provide a reference to the other outer class object in the new
expression, like this:
|
 |
On peut demander à un autre objet de créer un objet de l'une de ses classes
internes. Pour cela il faut fournir une référence à l'autre objet de la classe externe dans
l'expression new, comme ceci :
|
 |
 |
 |
//: c08:Parcel11.java
// Creating instances of inner classes.
public class Parcel11 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public static void main(String[] args) {
Parcel11 p = new Parcel11();
// Must use instance of outer class
// to create an instances of the inner class:
Parcel11.Contents c = p.new Contents();
Parcel11.Destination d =
p.new Destination("Tanzania");
}
} ///:~
|
 |
//: c08:Parcel11.java // Création d'instances de classes internes.
public class Parcel11 { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String whereTo) { label = whereTo; } String readLabel() { return label; } } public static void main(String[] args) { Parcel11 p = new Parcel11(); // On doit utiliser une instance de la classe externe // pour créer une instance de la classe interne : Parcel11.Contents c = p.new Contents(); Parcel11.Destination d = p.new Destination("Tanzania"); } } ///:~
|
 |
 |
 |
To create an object of the inner class
directly, you don’t follow the same form and refer to the outer class name
Parcel11 as you might expect, but instead you must use an object
of the outer class to make an object of the inner class:
|
 |
Pour créer un objet de la classe interne directement, il ne faut pas
utiliser la même syntaxe et se référer au nom de la classe externe Parcel11 comme
on pourrait s'y attendre ; à la place il faut utiliser un objet de la classe externe pour
créer un objet de la classe interne :
|
 |
 |
 |
Parcel11.Contents c = p.new Contents();
|
 |
Parcel11.Contents c = p.new Contents();
|
 |
 |
 |
Thus, it’s not possible to create
an object of the inner class unless you already have an object of the outer
class. This is because the object of the inner class is quietly connected to the
object of the outer class that it was made from. However, if you make a
static inner class, then it doesn’t need a reference to the outer
class object.
|
 |
Il n'est donc pas possible de créer un objet de la classe interne sans
disposer déjà d'un objet de la classe externe, parce qu'un objet de la classe interne est toujours
connecté avec l'objet de la classe externe qui l'a créé. Cependant, si la classe interne est
static, elle n'a pas besoin d'une référence sur un objet de la classe
externe.
|
 |
 |
 |
Reaching outward from a multiply-nested class
|
 |
Classe interne à plusieurs niveaux d'imbrication
|
 |
 |
 |
[41]It
doesn’t matter how deeply an inner class may be nested—it can
transparently access all of the members of all the classes it is nested within,
as seen here:
|
 |
[41]Une classe interne peut se situer à
n'importe quel niveau d'imbrication - elle pourra toujours accéder de manière transparente à tous
les membres de toutes les classes l'entourant, comme on peut le voir :
|
 |
 |
 |
//: c08:MultiNestingAccess.java
// Nested classes can access all members of all
// levels of the classes they are nested within.
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
} ///:~
|
 |
//: c08:MultiNestingAccess.java // Les classes imbriquées peuvent accéder à tous les membres de tous // les niveaux des classes dans lesquelles elles sont imbriquées.
class MNA { private void f() {} class A { private void g() {} public class B { void h() { g(); f(); } } } }
public class MultiNestingAccess { public static void main(String[] args) { MNA mna = new MNA(); MNA.A mnaa = mna.new A(); MNA.A.B mnaab = mnaa.new B(); mnaab.h(); } } ///:~
|
 |
 |
 |
You can see that in MNA.A.B, the
methods g( ) and f( ) are callable without any
qualification (despite the fact that they are private). This example also
demonstrates the syntax necessary to create objects of multiply-nested inner
classes when you create the objects in a different class. The
“.new” syntax produces the correct scope so you do not have
to qualify the class name in the constructor
call.
|
 |
On peut voir que dans MNA.A.B, les méthodes
g() et f() sont appelées sans qualification (malgré le fait
qu'elles soient private). Cet exemple présente aussi la syntaxe utilisée pour
créer des objets de classes internes imbriquées quand on crée ces objets depuis une autre classe.
La syntaxe « .new » fournit la portée correcte et on n'a donc pas besoin
de qualifier le nom de la classe dans l'appel du constructeur.
|
 |
 |
 |
Inheriting from inner
classes
|
 |
Dériver une classe interne
|
 |
 |
 |
Because the inner class constructor must
attach to a reference of the enclosing class object, things are slightly
complicated when you inherit from an inner class. The problem is that the
“secret” reference to the enclosing class object must be
initialized, and yet in the derived class there’s no longer a default
object to attach to. The answer is to use a syntax provided to make the
association explicit:
|
 |
Comme le constructeur d'une classe interne doit stocker une référence à
l'objet de la classe externe, les choses sont un peu plus compliquées lorsqu'on dérive une classe
interne. Le problème est que la référence « secrète » sur l'objet de la classe externe
doit être initialisée, et dans la classe dérivée il n'y a plus d'objet sur lequel se
rattacher par défaut. Il faut donc utiliser une syntaxe qui rende cette association explicite
:
|
 |
 |
 |
//: c08:InheritInner.java
// Inheriting an inner class.
class WithInner {
class Inner {}
}
public class InheritInner
extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
} ///:~
|
 |
//: c08:InheritInner.java // Inheriting an inner class.
class WithInner { class Inner {} }
public class InheritInner extends WithInner.Inner { //! InheritInner() {} // Ne compilera pas. InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } } ///:~
|
 |
 |
 |
You can see that InheritInner is
extending only the inner class, not the outer one. But when it comes time to
create a constructor, the default one is no good and you can’t just pass a
reference to an enclosing object. In addition, you must use the
syntax
|
 |
On peut voir que InheritInner étend juste la classe
interne, et non la classe externe. Mais lorsqu'on en arrive au constructeur, celui fourni par
défaut n'est pas suffisant et on ne peut se contenter de passer une référence à un objet externe.
De plus, on doit utiliser la syntaxe :
|
 |
 |
 |
enclosingClassReference.super();
|
 |
enclosingClassReference.super();
|
 |
 |
 |
inside
the constructor. This provides the necessary reference and the program will then
compile.
|
 |
à l'intérieur du constructeur. Ceci fournit la référence nécessaire et le
programme pourra alors être compilé.
|
 |
 |
 |
Can inner classes be overridden?
|
 |
Les classes internes peuvent-elles redéfinies ?
|
 |
 |
 |
What happens when you create an inner
class, then inherit from the enclosing class and redefine the inner class? That
is, is it possible to override an inner class? This seems like it would be a
powerful concept, but
“overriding”
an inner class as if it were another method of the outer class doesn’t
really do anything:
|
 |
Que se passe-t-il quand on crée une classe interne, qu'on dérive la classe
externe et qu'on redéfinit la classe interne ? Autrement dit, est-il possible de rédéfinir une
classe interne ? Ce concept semble particulièrement puissant, mais « redéfinir » une classe
interne comme si c'était une méthode de la classe externe ne fait rien de spécial :
|
 |
 |
 |
//: c08:BigEgg.java
// An inner class cannot be overriden
// like a method.
class Egg {
protected class Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
private Yolk y;
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args) {
new BigEgg();
}
} ///:~
|
 |
//: c08:BigEgg.java // Une classe interne ne peut être // redéfinie comme une méthode.
class Egg { protected class Yolk { public Yolk() { System.out.println("Egg.Yolk()"); } } private Yolk y; public Egg() { System.out.println("New Egg()"); y = new Yolk(); } }
public class BigEgg extends Egg { public class Yolk { public Yolk() { System.out.println("BigEgg.Yolk()"); } } public static void main(String[] args) { new BigEgg(); } } ///:~
|
 |
 |
 |
The default constructor is synthesized
automatically by the compiler, and this calls the base-class default
constructor. You might think that since a BigEgg is being created, the
“overridden” version of Yolk would be used, but this is not
the case. The output is:
|
 |
Le constructeur par défaut est généré automatiquement par le compilateur,
et il appelle le constructeur par défaut de la classe de base. On pourrait penser que puisqu'on
crée un BigEgg, la version « redéfinie » de Yolk sera
utilisée, mais ce n'est pas le cas. La sortie produite est :
|
 |
 |
 |
New Egg()
Egg.Yolk()
|
 |
New Egg() Egg.Yolk()
|
 |
 |
 |
This example simply shows that there
isn’t any extra inner class magic going on when you inherit from the outer
class. The two inner classes are completely separate entities, each in their own
namespace. However, it’s still possible to explicitly inherit from the
inner class:
|
 |
Cet exemple montre simplement qu'il n'y a aucune magie spéciale associée
aux classes internes quand on hérite d'une classe externe. Les deux classes internes sont des
entités complètement séparées, chacune dans leur propre espace de noms. Cependant, il est toujours
possible de dériver explicitement la classe interne :
|
 |
 |
 |
//: c08:BigEgg2.java
// Proper inheritance of an inner class.
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy) { y = yy; }
public void g() { y.f(); }
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() { insertYolk(new Yolk()); }
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
} ///:~
|
 |
//: c08:BigEgg2.java // Dérivation d'une classe interne.
class Egg2 { protected class Yolk { public Yolk() { System.out.println("Egg2.Yolk()"); } public void f() { System.out.println("Egg2.Yolk.f()"); } } private Yolk y = new Yolk(); public Egg2() { System.out.println("New Egg2()"); } public void insertYolk(Yolk yy) { y = yy; } public void g() { y.f(); } }
public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk() { System.out.println("BigEgg2.Yolk()"); } public void f() { System.out.println("BigEgg2.Yolk.f()"); } } public BigEgg2() { insertYolk(new Yolk()); } public static void main(String[] args) { Egg2 e2 = new BigEgg2(); e2.g(); } } ///:~
|
 |
 |
 |
Now BigEgg2.Yolk explicitly
extends Egg2.Yolk and overrides its methods. The method
insertYolk( ) allows BigEgg2 to upcast one of its own Yolk
objects into the y reference in Egg2, so when g( )
calls y.f( ) the overridden version of f( ) is used. The
output is:
|
 |
Maintenant BiggEgg2.Yolk étend explicitement
Egg2.Yolk et redéfinit ses méthodes. La méthode insertYolk()
permet à BiggEgg2 de transtyper un de ses propres objets Yolk
dans la référence y de Egg2, donc quand g()
appelle y.f(), la version redéfinie de f() est utilisée. La
sortie du programme est :
|
 |
 |
 |
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
|
 |
Egg2.Yolk() New Egg2() Egg2.Yolk() BigEgg2.Yolk() BigEgg2.Yolk.f()
|
 |
 |
 |
The second call to
Egg2.Yolk( ) is the base-class constructor call of the
BigEgg2.Yolk constructor. You can see that the overridden version of
f( ) is used when g( ) is
called.
|
 |
Le second appel à Egg2.Yolk() est l'appel du constructeur
de la classe de base depuis le constructeur de BigEgg2.Yolk. On peut voir que la
version redéfinie de f() est utilisée lorsque g() est
appelée.
|
 |
 |
 |
Inner class identifiers
|
 |
Identifiants des classes internes
|
 |
 |
 |
Since every class produces a .class
file that holds all the information about how to create objects of this type
(this information produces a “meta-class” called the Class
object), you might guess that
inner classes must also produce
.class files to contain the information for their Class
objects. The names of these files/classes have a strict formula: the name of the
enclosing class, followed by a ‘$’, followed by the name of
the inner class. For example, the .class files created by
InheritInner.java include:
|
 |
Puisque chaque classe produit un fichier .class qui
contient toutes les informations concernant la création d'objets de ce type (ces informations
produisent une « méta-classe » dans un objet Class), il est aisé de deviner
que les classes internes produisent aussi des fichiers .class qui contiennent
des informations pour leurs objets Class. La nomenclature de ces fichiers
/ classes est stricte : le nom de la classe externe suivie par un $, suivi du nom
de la classe interne. Par exemple, les fichiers .class créés par
InheritInner.java incluent :
|
 |
 |
 |
InheritInner.class
WithInner$Inner.class
WithInner.class
|
 |
InheritInner.class WithInner$Inner.class WithInner.class
|
 |
 |
 |
If inner classes are anonymous, the
compiler simply starts generating numbers as inner class identifiers. If inner
classes are nested within inner classes, their names are simply appended after a
‘$’ and the outer class identifier(s).
|
 |
Si les classes internes sont anonymes, le compilateur génère simplement des
nombres comme identifiants de classe interne. Si des classes internes sont imbriquées dans d'autres
classes internes, leur nom est simplement ajouté après un $ et le nom des
identifiants des classes externes.
|
 |
 |
 |
Although this scheme of generating
internal names is simple and straightforward, it’s also robust and handles
most
situations[42].
Since it is the standard naming scheme for Java, the generated files are
automatically platform-independent. (Note that the Java compiler is changing
your inner classes in all sorts of other ways in order to make them
work.)
|
 |
Bien que cette gestion interne des noms soit simple et directe, elle est robuste et gère la
plupart des situations [42]. Et comme cette notation est la
notation standard pour Java, les fichiers générés sont automatiquement indépendants de la
plateforme (Notez que le compilateur Java modifie les classes internes d'un tas d'autres manières
afin de les faire fonctionner).
|
 |
 |
 |
Why inner classes?
|
 |
Raison d'être des classes internes
|
 |
 |
 |
At this point you’ve seen a lot of
syntax and semantics describing the way inner classes work, but this
doesn’t answer the question of why they exist. Why did Sun go to so much
trouble to add this fundamental language feature?
|
 |
Jusqu'à présent, on a vu la syntaxe et la sémantique décrivant la façon
dont les classes internes fonctionnent, mais cela ne répond pas à la question du pourquoi de leur
existence. Pourquoi Sun s'est-il donné tant de mal pour ajouter au langage cette fonctionnalité
fondamentale ?
|
 |
 |
 |
Typically, the inner class inherits from
a class or implements an interface, and the code in the inner class
manipulates the outer class object that it was created within. So you could say
that an inner class provides a kind of window into the outer
class.
|
 |
Typiquement, la classe interne hérite d'une classe ou implémente une
interface, et le code de la classe interne manipule l'objet de la classe externe
l'ayant créé. On peut donc dire qu'une classe interne est une sorte de fenêtre dans la classe
externe.
|
 |
 |
 |
A question that cuts to the heart of
inner classes is this: if I just need a reference to an interface, why
don’t I just make the outer class implement that interface? The
answer is “If that’s all you need, then that’s how you should
do it.” So what is it that distinguishes an inner class implementing an
interface from an outer class implementing the same interface? The
answer is that you can’t always have the convenience of
interfaces—sometimes you’re working with implementations. So
the most compelling reason for inner classes is:
|
 |
Mais si on a juste besoin d'une référence sur une
interface, pourquoi ne pas implémenter cette interface
directement dans la classe externe ? La réponse à cette question allant au coeur des classes
internes est simple : « Si c'est tout ce dont on a besoin, alors c'est ainsi qu'il faut
procéder ». Alors qu'est-ce qui distingue une classe interne implémentant une
interface d'une classe externe implémentant cette même interface
? C'est tout simplement qu'on ne dispose pas toujours des facilités fournies par les
interfaces - quelquefois on est obligé de travailler avec des implémentations.
Voici donc la raison principale d'utiliser des classes internes :
|
 |
 |
 |
Each inner class can independently
inherit from an implementation. Thus, the inner class is not limited by whether
the outer class is already inheriting from an
implementation.
|
 |
Chaque classe interne peut hériter indépendamment d'une implémentation.
La classe interne n'est pas limitée par le fait que la classe externe hérite déjà d'une
implémentation.
|
 |
 |
 |
Without the ability that inner classes
provide to inherit—in effect—from more than one concrete or
abstract class, some design and programming problems would be
intractable. So one way to look at the inner class is as the completion of the
solution of the multiple-inheritance problem. Interfaces solve part of the
problem, but inner classes effectively allow “multiple implementation
inheritance.” That is, inner classes effectively allow you to inherit from
more than one non-interface.
|
 |
Sans cette capacité que fournissent les classes internes d'hériter - dans
la pratique - de plus d'une classe concrète ou abstract, certaines conceptions ou
problèmes seraient impossibles à résoudre. Les classes internes peuvent donc être considérées comme
la suite de la solution au problème de l'héritage multiple. Les interfaces résolvent une partie du
problème, mais les classes internes permettent réellement « l'héritage multiple
d'implémentations ». Les classes internes permettent effectivement de dériver plusieurs non
interfaces.
|
 |
 |
 |
To see this in more detail, consider a
situation where you have two interfaces that must somehow be implemented within
a class. Because of the flexibility of interfaces, you have two choices: a
single class or an inner class:
|
 |
Pour voir ceci plus en détails, imaginons une situation dans laquelle une
classe doit implémenter deux interfaces. Du fait de la flexibilité des interfaces, on a le choix
entre avoir une classe unique ou s'aider d'une classe interne :
|
 |
 |
 |
//: c08:MultiInterfaces.java
// Two ways that a class can
// implement multiple interfaces.
interface A {}
interface B {}
class X implements A, B {}
class Y implements A {
B makeB() {
// Anonymous inner class:
return new B() {};
}
}
public class MultiInterfaces {
static void takesA(A a) {}
static void takesB(B b) {}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
} ///:~
|
 |
//: c08:MultiInterfaces.java // Deux façons pour une classe // d'implémenter des interfaces multiples.
interface A {} interface B {}
class X implements A, B {}
class Y implements A { B makeB() { // Classe interne anonyme : return new B() {}; } }
public class MultiInterfaces { static void takesA(A a) {} static void takesB(B b) {} public static void main(String[] args) { X x = new X(); Y y = new Y(); takesA(x); takesA(y); takesB(x); takesB(y.makeB()); } } ///:~
|
 |
 |
 |
Of course, this assumes that the
structure of your code makes logical sense either way. However, you’ll
ordinarily have some kind of guidance from the nature of the problem about
whether to use a single class or an inner class. But without any other
constraints, in the above example the approach you take doesn’t really
make much difference from an implementation standpoint. Both of them
work.
|
 |
Bien sûr, la structure du code peut impliquer une logique pouvant imposer
l'une ou l'autre des solutions. La nature du problème fournit généralement aussi des indices pour
choisir entre une classe unique ou une classe interne. Mais en l'absence d'aucune autre contrainte,
l'approche choisie dans l'exemple précédent ne fait aucune différence du point de vue
implémentation. Les deux fonctionnent.
|
 |
 |
 |
However, if you have abstract or
concrete classes instead of interfaces, you are suddenly limited to using
inner classes if your class must somehow implement both of the
others:
|
 |
Cependant, si on a des classes abstract ou concrètes à la
place des interfaces, on est obligé de recourir aux classes internes si la classe
doit implémenter les deux :
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |