 |
 |
6) Réutiliser les classes |
|
 |
|
Texte original |
 |
Traducteur : Olivier THOMANN |
|
 |
|
Ce chapitre contient 4 pages
1
2
3
4
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
05.07.01 - version 1.2 [Armel] : - Ajout des tags de séparation des pages. 25.04.2001 - version 1.1 : - Mise en forme du code html (titres-hx[verdana], paragraphes-p[Georgia], code-blockquote). Traducteur : - Olivier THOMANN Texte original : -Thinking in Java, 2nd edition, Revision 10 © 2000 by Bruce Eckel
|
 |
 |
 |
6: Reusing Classes
|
 |
6 : Réutiliser les classes
|
 |
 |
 |
One of the most compelling
features about Java is code reuse.
But to be revolutionary, you’ve got to be able to do a lot more than copy
code and change it.
|
 |
Une des caractéristiques les plus excitantes de Java est la
réutilisation du code. Mais pour être vraiment révolutionnaire, il faut faire plus que copier du
code et le changer.
|
 |
 |
 |
That’s the approach used in
procedural languages like C, and it hasn’t worked very well. Like
everything in Java, the solution revolves around the class. You reuse code by
creating new classes, but instead of creating them from scratch, you use
existing classes that someone has already built and debugged.
|
 |
C'est l'approche utilisée dans les langages procéduraux comme C, et ça n'a
pas très bien fonctionné. Comme tout en Java, la solution réside dans les classes. On réutilise du
code en créant de nouvelles classes, mais au lieu de les créer depuis zéro, on utilise les classes
que quelqu'un a construit et testé.
|
 |
 |
 |
The trick is to use the classes without
soiling the existing code. In this chapter you’ll see two ways to
accomplish this. The first is quite straightforward: You simply create objects
of your existing class inside the new class. This is called composition,
because the new class is composed of objects of existing
classes. You’re simply reusing the functionality of the code, not its
form.
|
 |
L'astuce est d'utiliser les classes sans détériorer le code existant. Dans
ce chapitre nous verrons deux manières de faire. La première est plutôt directe : On crée
simplement des objets de nos classes existantes à l'intérieur de la nouvelle classe. Ça s'appelle
la composition, parce que la nouvelle classe se compose d'objets de classes existantes. On
réutilise simplement les fonctionnalités du code et non sa forme.
|
 |
 |
 |
The second approach is more subtle. It
creates a new class as a type of an existing class. You literally take
the form of the existing class and add code to it without modifying the existing
class. This magical act is called inheritance, and
the compiler does most of the work. Inheritance is one of the cornerstones of
object-oriented programming, and has additional implications that will be
explored in Chapter 7.
|
 |
La seconde approche est plus subtile. On crée une nouvelle classe comme un
type d'une classe existante. On prend littéralement la forme d'une classe existante et on
lui ajoute du code sans modifier la classe existante. Cette magie s'appelle l'héritage, et
le compilateur fait le plus gros du travail. L'héritage est une des pierres angulaires de la
programmation par objets, et a bien d'autres implications qui seront explorées au chapitre
7.
|
 |
 |
 |
It turns out that much of the syntax and
behavior are similar for both composition and inheritance (which makes sense
because they are both ways of making new types from existing types). In this
chapter, you’ll learn about these code reuse
mechanisms.
|
 |
Il s'avère que beaucoup de la syntaxe et du comportement sont identiques
pour la composition et l' héritage (cela se comprend parce qu'ils sont tous deux des moyens de
construire des nouveaux types à partir de types existants). Dans ce chapitre, nous apprendrons ces
mécanismes de réutilisation de code.
|
 |
 |
 |
Composition syntax
|
 |
Syntaxe de composition
|
 |
 |
 |
Until now, composition has been used
quite frequently. You simply place object references inside new classes. For
example, suppose you’d like an object that holds several String
objects, a couple of primitives, and an object of another class. For the
nonprimitive objects, you put references inside your new class, but you define
the primitives directly:
|
 |
Jusqu'à maintenant, la composition a été utilisée assez fréquemment. On
utilise simplement des références sur des objets dans de nouvelles classes. Par exemple, supposons
que l'on souhaite un objet qui contient plusieurs objets de type String, quelques
types primitifs et un objet d'une autre classe. Pour les objets, on met des références à
l'intérieur de notre nouvelle classe, mais on définit directement les types primitifs:
|
 |
 |
 |
//: c06:SprinklerSystem.java // Composition for code reuse.
class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } }
public class SprinklerSystem { private String valve1, valve2, valve3, valve4; WaterSource source; int i; float f; void print() { System.out.println("valve1 = " + valve1); System.out.println("valve2 = " + valve2); System.out.println("valve3 = " + valve3); System.out.println("valve4 = " + valve4); System.out.println("i = " + i); System.out.println("f = " + f); System.out.println("source = " + source); } public static void main(String[] args) { SprinklerSystem x = new SprinklerSystem(); x.print(); } } ///:~
|
 |
// ! c06:SprinklerSystem.java // La composition pour réutiliser du code.
class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } }
public class SprinklerSystem { private String valve1, valve2, valve3, valve4; WaterSource source; int i; float f; void print() { System.out.println("valve1 = " + valve1); System.out.println("valve2 = " + valve2); System.out.println("valve3 = " + valve3); System.out.println("valve4 = " + valve4); System.out.println("i = " + i); System.out.println("f = " + f); System.out.println("source = " + source); } public static void main(String[] args) { SprinklerSystem x = new SprinklerSystem(); x.print(); } } ///:~
|
 |
 |
 |
One of the methods defined in
WaterSource is special: toString( ). You will learn later
that every nonprimitive object has a
toString( ) method,
and it’s called in special situations when the compiler wants a
String but it’s got one of these objects. So in the
expression:
|
 |
Une des méthodes définies dans WaterSource est
spéciale : toString( ). Vous apprendrez plus tard que chaque type
non primitif a une méthode toString( ), et elle est appelée dans des
situations spéciales lorsque le compilateur attend une String alors qu'il ne
trouve qu'un objet. Donc dans une expression:
|
 |
 |
 |
System.out.println("source = " + source);
|
 |
System.out.println("source = " + source);
|
 |
 |
 |
the compiler sees you trying to add a String object ("source = ") to a WaterSource. This doesn’t make sense to it, because you can only “add” a String to another String, so it says “I’ll turn source into a String by calling toString( )!” After doing this it can combine the two Strings and pass the resulting String to System.out.println( ). Any time you want to allow this behavior with a class you create you need only write a toString( ) method.
|
 |
le compilateur voit que vous essayez d'ajouter un objet
String ("source = ") à un WaterSource. Ceci n'a
pas de sens parce qu'on peut seulement ajouter une String à une autre
String, donc il se dit qu'il va convertir source en une
String en appelant toString( ) ! Après avoir fait cela
il combine les deux Strings et passe la String résultante à
System.out.println( ). Dès qu'on veut permettre ce comportement avec une
classe qu'on crée, il suffit simplement de définir une méthode
toString( ).
|
 |
 |
 |
At first glance, you might assume—Java being as safe and careful as it is—that the compiler would automatically construct objects for each of the references in the above code; for example, calling the default constructor for WaterSource to initialize source. The output of the print statement is in fact:
|
 |
Au premier regard, on pourrait supposer — Java étant sûr et
prudent comme il l'est — que le compilateur construirait automatiquement des objets pour
chaque référence dans le code ci-dessus ; par exemple, en appelant le constructeur par
défaut pour WaterSource pour initialiser source. Le résultat de
l'instruction d'impression affiché est en fait :
|
 |
 |
 |
valve1 = null valve2 = null valve3 = null valve4 = null i = 0 f = 0.0 source = null
|
 |
valve1 = null valve2 = null valve3 = null valve4 = null i = 0 f = 0.0 source = null
|
 |
 |
 |
Primitives that are fields in a class are
automatically initialized to zero,
as noted in Chapter 2. But the object references are initialized to null,
and if you try to call methods for any of them you’ll get an exception.
It’s actually pretty good (and useful) that you can still print them out
without throwing an exception.
|
 |
Les types primitifs qui sont des champs d'une classe sont automatiquement
initialisés à zéro, comme précisé dans le chapitre 2. Mais les références objet sont initialisées à
null, et si on essaye d'appeler des méthodes pour l'un d'entre eux, on obtient une
exception. En fait il est bon (et utile) qu'on puisse les afficher sans lancer
d'exception.
|
 |
 |
 |
It makes sense that the compiler
doesn’t just create a default object for every reference because that
would incur unnecessary overhead in many cases. If you want the references
initialized, you can do it:
|
 |
On comprend bien que le compilateur ne crée pas un objet par défaut pour
chaque référence parce que cela induirait souvent une surcharge inutile. Si on veut initialiser les
références, on peut faire :
|
 |
 |
 |
- At the point the objects
are defined. This means that they’ll always be initialized before the
constructor is
called.
- In the
constructor for that
class.
- Right before
you actually need to use the object. This is often called lazy
initializatio
|
 |
- Au moment où les objets sont définis. Cela signifie qu'ils seront toujours
initialisés avant que le constructeur ne soit appelé ;
- Dans le constructeur pour la classe ;
- Juste avant d'utiliser l'objet, ce qui est souvent appelé
initialisation paresseuse.
|
 |
 |
 |
n.
It can reduce overhead in situations where the object doesn’t need to be
created every time.
|
 |
Cela peut réduire la surcharge dans les situations où l'objet n'a pas
besoin d'être créé à chaque fois.
|
 |
 |
 |
All three approaches are shown
here:
|
 |
Les trois approches sont montrées ici:
|
 |
 |
 |
//: c06:Bath.java // Constructor initialization with composition.
class Soap { private String s; Soap() { System.out.println("Soap()"); s = new String("Constructed"); } public String toString() { return s; } }
public class Bath { private String // Initializing at point of definition: s1 = new String("Happy"), s2 = "Happy", s3, s4; Soap castille; int i; float toy; Bath() { System.out.println("Inside Bath()"); s3 = new String("Joy"); i = 47; toy = 3.14f; castille = new Soap(); } void print() { // Delayed initialization: if(s4 == null) s4 = new String("Joy"); System.out.println("s1 = " + s1); System.out.println("s2 = " + s2); System.out.println("s3 = " + s3); System.out.println("s4 = " + s4); System.out.println("i = " + i); System.out.println("toy = " + toy); System.out.println("castille = " + castille); } public static void main(String[] args) { Bath b = new Bath(); b.print(); } } ///:~
|
 |
// ! c06:Bath.java // Initialisation dans le constructeur avec composition.
class Soap { private String s; Soap() { System.out.println("Soap()"); s = new String("Constructed"); } public String toString() { return s; } }
public class Bath { private String // Initialisation au moment de la définition: s1 = new String("Happy"), s2 = "Happy", s3, s4; Soap castille; int i; float toy; Bath() { System.out.println("Inside Bath()"); s3 = new String("Joy"); i = 47; toy = 3.14f; castille = new Soap(); } void print() { // Initialisation différée: if(s4 == null) s4 = new String("Joy"); System.out.println("s1 = " + s1); System.out.println("s2 = " + s2); System.out.println("s3 = " + s3); System.out.println("s4 = " + s4); System.out.println("i = " + i); System.out.println("toy = " + toy); System.out.println("castille = " + castille); } public static void main(String[] args) { Bath b = new Bath(); b.print(); } } ///:~
|
 |
 |
 |
Note that in the Bath constructor
a statement is executed before any of the initializations take place. When you
don’t initialize at the point of definition, there’s still no
guarantee that you’ll perform any initialization before you send a message
to an object reference—except for the inevitable run-time
exception.
|
 |
Notez que dans le constructeur de Bath une instruction est
exécutée avant que toute initialisation ait lieu. Quand on n'initialise pas au moment de la
définition, il n'est pas encore garanti qu'on exécutera une initialisation avant qu'on envoie un
message à un objet — sauf l'inévitable exception à l'exécution.
|
 |
 |
 |
Here’s the output for the
program:
|
 |
Ici la sortie pour le programme est :
|
 |
 |
 |
Inside Bath() Soap() s1 = Happy s2 = Happy s3 = Joy s4 = Joy i = 47 toy = 3.14 castille = Constructed
|
 |
Inside Bath() Soap() s1 = Happy s2 = Happy s3 = Joy s4 = Joy i = 47 toy = 3.14 castille = Constructed
|
 |
 |
 |
When print( ) is called it
fills in s4 so that all the fields are properly initialized by the time
they are
used.
|
 |
Quand print( ) est appelé, il remplit
s4 donc tout les champs sont proprement initialisés au moment où ils sont
utilisés.
|
 |
 |
 |
Inheritance syntax
|
 |
La syntaxe de l'héritage
|
 |
 |
 |
Inheritance is an integral part of Java
(and OOP languages in general). It turns out that you’re always doing
inheritance when you create a class, because unless you explicitly inherit from
some other class, you implicitly inherit from Java’s
standard root class Object.
|
 |
L'héritage est une partie primordiale de Java (et des langages de
programmation par objets en général). Il s'avère qu'on utilise toujours l'héritage quand on veut
créer une classe, parce qu'à moins d'hériter explicitement d'une autre classe, on hérite
implicitement de la classe racine standard Object.
|
 |
 |
 |
The syntax for composition is obvious,
but to perform inheritance there’s a distinctly different form. When you
inherit, you say “This new class is like that old class.” You state
this in code by giving the name of the class as usual, but before the opening
brace of the class body, put the keyword extends
followed by the name of the
base class. When you do
this, you automatically get all the data members and methods in the base class.
Here’s an example:
|
 |
La syntaxe de composition est évidente, mais pour réaliser l'héritage il y
a une forme distinctement différente. Quand on hérite, on dit « Cette nouvelle classe est
comme l'ancienne classe ». On stipule ceci dans le code en donnant le nom de la classe comme
d'habitude, mais avant l'accolade ouvrante du corps de la classe, on met le mot clé
extends suivi par le nom de la classe de base. Quand on fait cela, on
récupère automatiquement toutes les données membres et méthodes de la classe de base. Voici un
exemple:
|
 |
 |
 |
//: c06:Detergent.java // Inheritance syntax & properties.
class Cleanser { private String s = new String("Cleanser"); public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public void print() { System.out.println(s); } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); x.print(); } }
public class Detergent extends Cleanser { // Change a method: public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Add methods to the interface: public void foam() { append(" foam()"); } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Testing base class:"); Cleanser.main(args); } } ///:~
|
 |
// ! c06:Detergent.java // Syntaxe d'héritage & propriétés.
class Cleanser { private String s = new String("Cleanser"); public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public void print() { System.out.println(s); } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); x.print(); } }
public class Detergent extends Cleanser { // Change une méthode: public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Appel de la version de la classe de base } // Ajoute une méthode à l'interface: public void foam() { append(" foam()"); } // Test de la nouvelle classe: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); x.print(); System.out.println("Testing base class:"); Cleanser.main(args); } } ///:~
|
 |
 |
 |
This demonstrates a number of features.
First, in the Cleanser append( ) method, Strings are
concatenated to s using the += operator, which is one of the
operators (along with ‘+’) that the Java designers
“overloaded” to work with
Strings.
|
 |
Ceci montre un certain nombre de caractéristiques. Premièrement, dans
Cleanser la méthode append( ), les Strings
sont concaténées dans s en utilisant l'opérateur +=, qui est l'un
des opérateurs (avec « + ») que les créateurs de Java « ont
surchargé » pour travailler avec les Strings.
|
 |
 |
 |
Second, both Cleanser and
Detergent contain a main( ) method.
You can create a main( ) for each one of your classes, and
it’s often recommended to code this way so that your test code is wrapped
in with the class. Even if you have a lot of classes in a program, only the
main( ) for the class invoked on the command line will be called.
(As long as main( ) is public, it doesn’t matter
whether the class that it’s part of is public.) So in this case,
when you say java Detergent, Detergent.main( ) will be
called. But you can also say java Cleanser to invoke
Cleanser.main( ), even though Cleanser is not a public
class. This technique of putting a main( ) in each class allows easy
unit testing for each class. And
you don’t need to remove the main( ) when you’re
finished testing; you can leave it in for later testing.
|
 |
Deuxièmement, tant Cleanser que Detergent
contiennent une méthode main( ). On peut créer une
main( ) pour chacune de nos classes, et il est souvent recommandé de coder de
cette manière afin de garder le code de test dans la classe. Même si on a beaucoup de classes dans
un programme, seulement la méthode main( ) pour une classe invoquée sur la
ligne de commande sera appelée. Aussi longtemps que main( ) est
public, il importe peu que la classe dont elle fait partie soit
public ou non. Donc dans ce cas, quand on écrit java Detergent,
Detergent.main( ) sera appelée. Mais on peut également écrire java
Cleanser pour invoquer Cleanser.main( ), même si
Cleanser n'est pas une classe public. Cette technique de mettre
une main( ) dans chaque classe permet de tester facilement chaque classe. Et
on n'a pas besoin d'enlever la méthode main( ) quand on a finit de
tester ; on peut la laisser pour tester plus tard.
|
 |
 |
 |
Here, you can see that
Detergent.main( ) calls Cleanser.main( ) explicitly,
passing it the same arguments from the command line (however, you could pass it
any String array).
|
 |
Ici, on peut voir que Detergent.main( ) appelle
Cleanser.main( ) explicitement, en passant les même arguments depuis la ligne
de commande (quoiqu'il en soit, on peut passer n'importe quel tableau de
String).
|
 |
 |
 |
It’s important that all of the
methods in Cleanser are public. Remember that if you leave off any
access specifier the member defaults to “friendly,” which allows
access only to package members. Thus, within this package, anyone could
use those methods if there were no access specifier. Detergent would have
no trouble, for example. However, if a class from some other package were to
inherit from Cleanser it could access only public members. So to
plan for inheritance, as a general rule make all fields private and all
methods public. (protected members also allow access by
derived classes; you’ll learn about this later.) Of course, in particular
cases you must make adjustments, but this is a useful
guideline.
|
 |
Il est important que toutes les méthodes de Cleanser
soient public. Il faut se souvenir que si on néglige tout modifieur d'accès, par
défaut l'accès sera « friendly », lequel permet d'accéder seulement aux membres du même
package. Donc, au sein d'un même package, n'importe qui peut utiliser ces méthodes s'il
n'y a pas de spécificateur d'accès. Detergent n'aurait aucun problème, par
exemple. Quoiqu'il en soit, si une classe d'un autre package devait hériter de
Cleanser il pourrait accéder seulement aux membres public. Donc
pour planifier l'héritage, en règle générale mettre tous les champs private et
toutes les méthodes public (les membres protected permettent
également d'accéder depuis une classe dérivée ; nous verrons cela plus tard). Bien sûr, dans
des cas particuliers on devra faire des ajustements, mais cela est une règle utile.
|
 |
 |
 |
Note that Cleanser has a set of
methods in its interface: append( ), dilute( ),
apply( ), scrub( ), and print( ). Because
Detergent is derived from Cleanser (via the
extends keyword) it automatically gets all these
methods in its interface, even though you don’t see them all explicitly
defined in Detergent. You can think of inheritance, then, as reusing
the interface. (The implementation also comes with it, but that part
isn’t the primary point.)
|
 |
Notez que Cleanser contient un ensemble de méthodes dans
son interface : append( ), dilute( ),
apply( ), scrub( ), et print( ).
Parce que Detergent est dérivé de Cleanser (à l'aide du
mot-clé extends) il récupère automatiquement toutes les méthodes de son interface
, même si elles ne sont pas toutes définies explicitement dans Detergent. On peut
penser à l'héritage comme à une réutilisation de l'interface (l'implémentation vient
également avec elle, mais ceci n'est pas le point principal).
|
 |
 |
 |
As seen in scrub( ),
it’s possible to take a method that’s been defined in the base class
and modify it. In this case, you might want to call the method from the base
class inside the new version. But inside scrub( ) you cannot simply
call scrub( ), since that would produce a recursive call, which
isn’t what you want. To solve this problem Java has the
keyword super that refers to the
“superclass” that the current class has been
inherited from. Thus the expression super.scrub( ) calls the
base-class version of the method scrub( ).
|
 |
Comme vu dans scrub( ), il est possible de prendre
une méthode qui a été définie dans la classe de base et la modifier. Dans ce cas, on pourrait
vouloir appeler la méthode de la classe de base dans la nouvelle version. Mais à l'intérieur de
scrub( ) on ne peut pas simplement appeler scrub( ),
car cela produirait un appel récursif, ce qui n'est pas ce que l'on veut. Pour résoudre ce
problème, Java a le mot-clé super qui réfère à la super classe de la classe
courante. Donc l'expression super.scrub( ) appelle la version de la classe de
base de la méthode scrub( ).
|
 |
 |
 |
When inheriting you’re not
restricted to using the methods of the base class. You can also add new methods
to the derived class exactly the way you put any method in a class: just define
it. The method foam( ) is an example of this.
|
 |
Quand on hérite, on n'est pas tenu de n'utiliser que les méthodes de la
classe de base. On peut également ajouter de nouvelles méthodes à la classe dérivée exactement de
la manière dont on met une méthode dans une classe : il suffit de la définir. La méthode
foam( ) en est un exemple.
|
 |
 |
 |
In Detergent.main( ) you can
see that for a Detergent object you can call all the methods that are
available in Cleanser as well as in Detergent (i.e.,
foam( )).
|
 |
Dans Detergent.main( ) on peut voir que pour un objet
Detergent on peut appeler toutes les méthodes disponible dans
Cleanser aussi bien que dans Detergent (e.g.,
foam( )).
|
 |
 |
 |
Initializing the base
class
|
 |
Initialiser la classe de base
|
 |
 |
 |
Since there are now two classes
involved—the base class and the
derived class—instead of
just one, it can be a bit confusing to try to imagine the resulting object
produced by a derived class. From the outside, it looks like the new class has
the same interface as the base class and maybe some additional methods and
fields. But inheritance doesn’t just copy the interface of the base class.
When you create an object of the derived class, it contains within it a
subobject of the base class. This
subobject is the same as if you
had created an object of the base class by itself. It’s just that, from
the outside, the subobject of the base class is wrapped within the derived-class
object.
|
 |
Depuis qu'il y a deux classes concernées - la classe de base et la classe dérivée - au lieu d'une seule, il peut être un peu troublant d'essayer
d'imaginer l'objet résultant produit par la classe dérivée. De l'extérieur, il semble que la
nouvelle classe a la même interface que la classe de base et peut-être quelques méthodes et champs
additionnels. Mais l'héritage ne se contente pas simplement de copier l'interface de la classe de
base. Quand on créée un objet de la classe dérivée, il contient en lui un sous-objet de la
classe de base. Ce sous-objet est le même que si on crée un objet de la classe de base elle-même.
C'est simplement que, depuis l'extérieur, le sous-objet de la classe de base est enrobé au sein de
l'objet de la classe dérivée.
|
 |
 |
 |
Of course, it’s essential that the
base-class subobject be initialized correctly and there’s only one way to
guarantee that: perform the initialization in the constructor, by calling the
base-class constructor, which has all the appropriate knowledge and privileges
to perform the base-class initialization. Java automatically inserts calls to
the base-class constructor in the derived-class constructor. The following
example shows this working with three levels of inheritance:
|
 |
Bien sûr, il est essentiel que le sous-objet de la classe de base soit
correctement initialisé et il y a un seul moyen de garantir cela: exécuter l'initialisation dans le
constructeur, en appelant la constructeur de la classe de base, lequel a tous les connaissances et
les privilèges appropriés pour exécuter l'initialisation de la classe de base. Java insère
automatiquement les appels au constructeur de la classe de base au sein du constructeur de la
classe dérivée. L'exemple suivant montre comment cela fonctionne avec 3 niveaux
d'héritage :
|
 |
 |
 |
//: c06:Cartoon.java // Constructor calls during inheritance.
class Art { Art() { System.out.println("Art constructor"); } }
class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } }
public class Cartoon extends Drawing { Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } ///:~
|
 |
// ! c06:Cartoon.java // Appels de constructeur durant l'initialisation
class Art { Art() { System.out.println("Art constructor"); } }
class Drawing extends Art { Drawing() { System.out.println("Drawing constructor"); } }
public class Cartoon extends Drawing { Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } ///:~
|
 |
 |
 |
The output for this program shows the
automatic calls:
|
 |
La sortie de ce programme montre les appels automatiques:
|
 |
 |
 |
Art constructor Drawing constructor Cartoon constructor
|
 |
Art constructor Drawing constructor Cartoon constructor
|
 |
 |
 |
You can see that the construction happens
from the base “outward,” so the base class is initialized before the
derived-class constructors can access it.
|
 |
On peut voir que la construction commence par la classe la plus haute dans
la hiérarchie, donc la classe de base est initialisée avant que les constructeurs de la classe
dérivée puisse y accéder.
|
 |
 |
 |
Even if you don’t create a
constructor for Cartoon( ), the compiler will
synthesize a default constructor for you that calls the
base class constructor.
|
 |
Même si on ne crée pas de constructeur pour
Cartoon( ), le compilateur fournira un constructeur.
|
 |
 |
 |
Constructors with arguments
|
 |
Constructeurs avec paramètres
|
 |
 |
 |
The above example has default
constructors; that is, they don’t have any
arguments. It’s easy for the compiler to call these because there’s
no question about what arguments to pass. If your class doesn’t have
default arguments, or if you want to call a base-class constructor that has an
argument, you must explicitly write the calls to the base-class constructor
using the super keyword and the appropriate
argument list:
|
 |
L'exemple ci-dessus a des constructeurs par défaut ; ils n'ont
pas de paramètres. C'est facile pour le compilateur d'appeler ceux-ci parce qu'il n'y a pas de
questions à se poser au sujet des arguments à passer. Si notre classe n'a pas de paramètres par
défaut, ou si on veut appeler le constructeur d'une classe de base avec paramètre, on doit
explicitement écrire les appels au contructeur de la classe de base en utilisant le mot clé super
ainsi que la liste de paramètres appropriée : super et la liste de
paramètres appropriée:
|
 |
 |
 |
//: c06:Chess.java // Inheritance, constructors and arguments.
class Game { Game(int i) { System.out.println("Game constructor"); } }
class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("BoardGame constructor"); } }
public class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); } } ///:~
|
 |
// ! c06:Chess.java // Héritage, constructeurs et paramètres.
class Game { Game(int i) { System.out.println("Game constructor"); } }
class BoardGame extends Game { BoardGame(int i) { super(i); System.out.println("BoardGame constructor"); } }
public class Chess extends BoardGame { Chess() { super(11); System.out.println("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); } } ///:~
|
 |
 |
 |
If you don’t call the base-class
constructor in BoardGame( ), the compiler will complain that it
can’t find a constructor of the form Game( ). In addition, the
call to the base-class constructor must be the first thing you do in the
derived-class constructor. (The compiler will remind you if you get it
wrong.)
|
 |
Si on n'appelle pas le constructeur de la classe de base dans
BoardGame( ), le compilateur va se plaindre qu'il ne peut pas trouver le
constructeur de la forme Game( ). De plus, l'appel du constructeur de la
classe de base doit être la première chose que l'on fait dans le constructeur de la classe
dérivée. Le compilateur va le rappeler si on se trompe.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |