t
t
t
t
t t 7) Polymorphisme
tttt
7) Polymorphisme
Texte original t Traducteur : Jérome DANNOVILLE
t
t
///
Ce chapitre contient 4 pages
1 2 3 4
\\\
t t t
t t t
t
t t t
The new methods are what( ), which returns a String reference with a description of the class, and adjust( ), which provides some way to adjust each instrument. t Les nouvelles méthodes sont what(), qui renvoie une référence sur une String décrivant la classe, et adjust(), qui fournit un moyen d'ajuster chaque instrument.
t t t
In main( ), when you place something inside the Instrument array you automatically upcast to Instrument. t Dans le main(), quand on met quelque chose dans le tableau d' Instrument, on upcast automatiquement en Instrument.
t t t
You can see that the tune( ) method is blissfully ignorant of all the code changes that have happened around it, and yet it works correctly. This is exactly what polymorphism is supposed to provide. Your code changes don’t cause damage to parts of the program that should not be affected. Put another way, polymorphism is one of the most important techniques that allow the programmer to “separate the things that change from the things that stay the same.” t Vous pouvez constater que la méthode tune() ignore fort heureusement tous les changements qui sont intervenus autour d'elle, et pourtant cela marche correctement. C'est exactement ce que le polymorphisme est censé fournir. Vos modifications ne peuvent abîmer les parties du programme qui ne devraient pas être affectées. Dit autrement, le polymorphisme est une des techniques majeures permettant au programmeur de « séparer les choses qui changent des choses qui restent les mêmes. »
t t t

Overriding vs. overloading

t

Redéfinition et Surcharge

t t t
Let’s take a different look at the first example in this chapter. In the following program, the interface of the method play( ) is changed in the process of overriding it, which means that you haven’t overridden the method, but instead overloaded it. The compiler allows you to overload methods so it gives no complaint. But the behavior is probably not what you want. Here’s the example: t Regardons sous un angle différent le premier exemple de ce chapitre. Dans le programme suivant, l'interface de la méthode play() est changée dans le but de la redéfinir, ce qui signifie que vous n'avez pas redéfinie la méthode, mais plutôt surchargée. Le compilateur vous permet de surcharger des méthodes, il ne proteste donc pas. Mais le comportement n'est probablement pas celui que vous vouliez. Voici l'exemple :
t t t
//: c07:WindError.java
// Accidentally changing the interface.

class NoteX {
  public static final int
    MIDDLE_C = 0, C_SHARP = 1, C_FLAT = 2;
}

class InstrumentX {
  public void play(int NoteX) {
    System.out.println("InstrumentX.play()");
  }
}

class WindX extends InstrumentX {
  // OOPS! Changes the method interface:
  public void play(NoteX n) {
    System.out.println("WindX.play(NoteX n)");
  }
}

public class WindError {
  public static void tune(InstrumentX i) {
    // ...
    i.play(NoteX.MIDDLE_C);
  }
  public static void main(String[] args) {
    WindX flute = new WindX();
    tune(flute); // Not the desired behavior!
  }
} ///:~
t
//: c07:WindError.java
// Changement accidentel de l'interface.
class NoteX {
  public static final int
    MIDDLE_C = 0, C_SHARP = 1, C_FLAT = 2;
}

class InstrumentX {
  public void play(int NoteX) {
    System.out.println("InstrumentX.play()");
  }
}

class WindX extends InstrumentX {
  // OUPS! L'interface de la méthode change:
  public void play(NoteX n) {
    System.out.println("WindX.play(NoteX n)");
  }
}

public class WindError {
  public static void tune(InstrumentX i) {
    // ...
    i.play(NoteX.MIDDLE_C);
  }
  public static void main(String[] args) {
    WindX flute = new WindX();
    tune(flute); // Ce n'est pas le comportement souhaité!
  }
} ///:~
t t t
There’s another confusing aspect thrown in here. In InstrumentX, the play( ) method takes an int that has the identifier NoteX. That is, even though NoteX is a class name, it can also be used as an identifier without complaint. But in WindX, play( ) takes a NoteX reference that has an identifier n. (Although you could even say play(NoteX NoteX) without an error.) Thus it appears that the programmer intended to override play( ) but mistyped the method a bit. The compiler, however, assumed that an overload and not an override was intended. Note that if you follow the standard Java naming convention, the argument identifier would be noteX (lowercase ‘n’), which would distinguish it from the class name. t Il y a un autre aspect déroutant ici. Dans InstrumentX, la méthode play() a pour argument un int identifié par NoteX. Bien que NoteX soit un nom de classe, il peut également être utilisé comme identificateur sans erreur. Mais dans WindX, play() prend une référence de NoteX qui a pour identificateur n (bien que vous puissiez même écrire play(NoteX NoteX) sans erreur). En fait, il s'avère que le programmeur a désiré redéfinir play() mais s'est trompé de type. Du coup le compilateur a supposé qu'une surcharge était souhaitée et non pas une redéfinition. Remarquez que si vous respectez la convention standard de nommage Java, l'identificateur d'argument serait noteX ('n' minuscule), ce qui le distinguerait du nom de la classe.
t t t
In tune, the InstrumentX i is sent the play( ) message, with one of NoteX’s members (MIDDLE_C) as an argument. Since NoteX contains int definitions, this means that the int version of the now-overloaded play( ) method is called, and since that has not been overridden the base-class version is used. t Dans tune, le message play() est envoyé à l'InstrumentX i, avec comme argument un de membres de NoteX (MIDDLE_C). Puisque NoteX contient des définitions d'int, ceci signifie que c'est la version avec int de la méthode play(), dorénavant surchargée, qui est appelée. Comme elle n'a pas été redéfinie, c'est donc la méthode de la classe de base qui est utilisée.
t t t
The output is: t L'output est le suivant :
t t t
InstrumentX.play()
t
InstrumentX.play()
t t t
This certainly doesn’t appear to be
a polymorphic method call. Once you understand what’s happening, you can
fix the problem fairly easily, but imagine how difficult it might be to find the
bug if it’s buried in a program of significant
size.
t Ceci n'est pas un appel polymorphe de méthode. Dès que vous comprenez ce qui se passe, vous pouvez corriger le problème assez facilement, mais imaginez la difficulté pour trouver l'anomalie si elle est enterrée dans un gros programme.
t t t


Abstract classes and methods


t

Classes et méthodes abstraites

t t t
In all the instrument examples, the
methods in the base class Instrument were always “dummy”
methods. If these methods are ever called, you’ve done something wrong.
That’s because the intent of Instrument is to create a common
interface
for all the classes derived from it.
t Dans tous ces exemples sur l'instrument de musique, les méthodes de la classe de base Instrument étaient toujours factices. Si jamais ces méthodes sont appelées, c'est que vous avez fait quelque chose de travers. C'est parce que le rôle de la classe Instrument est de créer une interface commune pour toutes les classes dérivées d'elle.
t t t
The only reason to establish this common
interface is so it can be
expressed differently for each different subtype. It establishes a basic form,
so you can say what’s in common with all the derived classes. Another way
of saying this is to call Instrument an abstract base class
(or simply
an abstract class). You create an abstract class when you want to
manipulate a set of classes through this common interface. All derived-class
methods that match the signature of the base-class declaration will be called
using the dynamic binding mechanism. (However, as seen in the last section, if
the method’s name is the same as the base class but the arguments are
different, you’ve got overloading, which probably isn’t what you
want.)
t La seule raison d'avoir cette interface commune est qu'elle peut être exprimée différemment pour chaque sous-type différent. Elle établit une forme de base, ainsi vous pouvez dire ce qui est commun avec toutes les classes dérivées. Une autre manière d'exprimer cette factorisation du code est d'appeler Instrument une classe de base abstraite (ou simplement une classe abstraite). Vous créez une classe abstraite quand vous voulez manipuler un ensemble de classes à travers cette interface commune. Toutes les méthodes des classes dérivées qui correspondent à la signature de la déclaration de classe de base seront appelées employant le mécanisme de liaison dynamique. (Cependant, comme on l'a vu dans la dernière section, si le nom de la méthode est le même comme la classe de base mais les arguments sont différents, vous avez une surcharge, ce qui n'est pas probablement que vous voulez.)
t t t
If you have an abstract class like
Instrument, objects of that class almost always have no meaning. That is,
Instrument is meant to express only the interface, and not a particular
implementation, so creating an Instrument object makes no sense, and
you’ll probably want to prevent the user from doing it. This can be
accomplished by making all the methods in Instrument print error
messages, but that delays the information until run-time and requires reliable
exhaustive testing on the user’s part. It’s always better to catch
problems at compile-time.
t Si vous avez une classe abstraite comme Instrument, les objets de cette classe n'ont pratiquement aucune signification. Le rôle d'Instrument est uniquement d'exprimer une interface et non pas une implémentation particulière, ainsi la création d'un objet Instrument n'a pas de sens et vous voudrez probablement dissuader l'utilisateur de le faire. Une implémentation possible est d'afficher un message d'erreur dans toutes les méthodes d'Instrument, mais cela retarde le diagnostic à l'exécution et exige un code fiable et exhaustif. Il est toujours préférable de traiter les problèmes au moment de la compilation.
t t t
Java provides a mechanism for doing this
called the abstract
method
[37].
This is a method that is incomplete; it has only a declaration and no method
body. Here is the syntax for an abstract method declaration:

t Java fournit un mécanisme qui implémente cette fonctionnalité: c'est la méthode abstraite [37]. C'est une méthode qui est incomplète; elle a seulement une déclaration et aucun corps de méthode. Voici la syntaxe pour une déclaration de méthode abstraite [abstract] :
t t t
abstract void f();
t
abstract void f();
t t t
A class containing abstract methods is
called an abstract class. If a class contains one or more abstract
methods, the class must be qualified as abstract. (Otherwise, the
compiler gives you an error message.)
t Une classe contenant des méthodes abstraites est appelée une classe abstraite. Si une classe contient une ou plusieurs méthodes abstraites, la classe doit être qualifiée comme abstract. (Autrement, le compilateur signale une erreur.)
t t t
If an abstract class is incomplete, what
is the compiler supposed to do when someone tries to make an object of that
class? It cannot safely create an object of an abstract class, so you get an
error message from the compiler. This way the compiler ensures the purity of the
abstract class, and you don’t need to worry about misusing
it.
t Si une classe abstraite est incomplète, comment doit réagir le compilateur si quelqu'un essaye de créer un objet de cette classe? Il ne peut pas créer sans risque un objet d'une classe abstraite, donc vous obtenez un message d'erreur du compilateur. C'est ainsi que le compilateur assure la pureté de la classe abstraite et ainsi vous n'avez plus a vous soucier d'un usage impropre de la classe.
t t t
If you
inherit
from an abstract class and you want to make objects of the new type, you must
provide method definitions for all the abstract methods in the base class. If
you don’t (and you may choose not to), then the derived class is also
abstract and the compiler will force you to qualify that class with the
abstract keyword.
t Si vous héritez d'une classe abstraite et que vous voulez fabriquer des objets du nouveau type, vous devez fournir des définitions de méthode correspondant à toutes les méthodes abstraites de la classe de base. Si vous ne le faites pas (cela peut être votre choix ), alors la classe dérivée est aussi abstraite et le compilateur vous forcera à qualifier cette classe avec le mot clé abstract.
t t t
It’s possible to create a class as
abstract without including any abstract methods. This is useful
when you’ve got a class in which it doesn’t make sense to have any
abstract methods, and yet you want to prevent any instances of that
class.
t Il est possible de créer une classe abstraite sans qu'elle contienne des méthodes abstraites. C'est utile quand vous avez une classe pour laquelle avoir des méthodes abstraites n'a pas de sens et que vous voulez empêcher la création d'instance de cette classe.
t t t
The Instrument class can easily be
turned into an abstract class. Only some of the methods will be
abstract, since making a class abstract doesn’t force you to make
all the methods abstract. Here’s what it looks
like:
t La classe Instrument peut facilement être changée en une classe abstraite. Seules certaines des méthodes seront abstraites, puisque créer une classe abstraite ne vous oblige pas a avoir que des méthodes abstraites. Voici à quoi cela ressemble :
t t t

t Image
t t t
Here’s the orchestra example
modified to use abstract classes and methods:

t Voici l'exemple de l'orchestre modifié en utilisant des classes et des méthodes abstraites :
t t t
//: c07:music4:Music4.java
// Abstract classes and methods.
import java.util.*;

abstract class Instrument {
  int i; // storage allocated for each
  public abstract void play();
  public String what() {
    return "Instrument";
  }
  public abstract void adjust();
}

class Wind extends Instrument {
  public void play() {
    System.out.println("Wind.play()");
  }
  public String what() { return "Wind"; }
  public void adjust() {}
}

class Percussion extends Instrument {
  public void play() {
    System.out.println("Percussion.play()");
  }
  public String what() { return "Percussion"; }
  public void adjust() {}
}

class Stringed extends Instrument {
  public void play() {
    System.out.println("Stringed.play()");
  }
  public String what() { return "Stringed"; }
  public void adjust() {}
}

class Brass extends Wind {
  public void play() {
    System.out.println("Brass.play()");
  }
  public void adjust() {
    System.out.println("Brass.adjust()");
  }
}

class Woodwind extends Wind {
  public void play() {
    System.out.println("Woodwind.play()");
  }
  public String what() { return "Woodwind"; }
}

public class Music4 {
  // Doesn't care about type, so new types
  // added to the system still work right:
  static void tune(Instrument i) {
    // ...
    i.play();
  }
  static void tuneAll(Instrument[] e) {
    for(int i = 0; i < e.length; i++)
      tune(e[i]);
  }
  public static void main(String[] args) {
    Instrument[] orchestra = new Instrument[5];
    int i = 0;
    // Upcasting during addition to the array:
    orchestra[i++] = new Wind();
    orchestra[i++] = new Percussion();
    orchestra[i++] = new Stringed();
    orchestra[i++] = new Brass();
    orchestra[i++] = new Woodwind();
    tuneAll(orchestra);
  }
} ///:~
t
//: c07:music4:Music4.java
// Classes et méthodes abstraites.
import java.util.*;

abstract class Instrument {
  int i; // Alloué à chaque fois
  public abstract void play();
  public String what() {
    return "Instrument"size="4">;
  }
  public abstract void adjust();
}

class Wind extends Instrument {
  public void play() {
    System.out.println("Wind.play()");
  }
  public String what() { return "Wind"; }
  public void adjust() {}
}

class Percussion extends Instrument {
  public void play() {
    System.out.println("Percussion.play()");
  }
  public String what() { return "Percussion"; }
  public void adjust() {}
}

class Stringed extends Instrument {
  public void play() {
    System.out.println("Stringed.play()");
  }
  public String what() { return "Stringed"; }
  public void adjust() {}
}

class Brass extends Wind {
  public void play() {
    System.out.println("Brass.play()");
  }
  public void adjust() {
    System.out.println("Brass.adjust()");
  }
}

class Woodwind extends Wind {
  public void play() {
    System.out.println("Woodwind.play()");
  }
  public String what() { return "Woodwind"; }
}

public class Music4 {
  // Ne se préoccupe pas des types: des nouveaux
  // ajoutés au système marcheront très bien:
  static void tune(Instrument i) {
    // ...
    i.play();
  }
  static void tuneAll(Instrument[] e) {
    forint i = 0; i < e.length; i++)
      tune(e[i]);
  }
  public static void main(String[] args) {
    Instrument[] orchestra = new Instrument[5];
    int i = 0;
    // Upcast lors de l'ajout au tableau:
    orchestra[i++] = new Wind();
    orchestra[i++] = new Percussion();
    orchestra[i++] = new Stringed();
    orchestra[i++] = new Brass();
    orchestra[i++] = new Woodwind();
    tuneAll(orchestra);
  }
} ///:~
t t t
You can see that there’s really no change except in the base class. t Vous pouvez voir qu'il n'y a vraiment aucun changement excepté dans la classe de base.
t t t
It’s helpful to create abstract classes and methods because they make the abstractness of a class explicit, and tell both the user and the compiler how it was intended to be used. t Il est utile de créer des classes et des méthodes abstraites parce qu'elles forment l'abstraction d'une classe explicite et indique autant à utilisateur qu'au compilateur comment elles doivent être utilisées.
t t t

Constructors and polymorphism

t

Constructeurs et polymorphisme

t t t
As usual, constructors are different from other kinds of methods. This is also true when polymorphism is involved. Even though constructors are not polymorphic (although you can have a kind of “virtual constructor,” as you will see in Chapter 12), it’s important to understand the way constructors work in complex hierarchies and with polymorphism. This understanding will help you avoid unpleasant entanglements. t Comme d'habitude, les constructeurs se comportent différemment des autres sortes de méthodes. C'est encore vrai pour le polymorphisme. Quoique les constructeurs ne soient pas polymorphes (bien que vous puissiez avoir un genre de "constructeur virtuel", comme vous le verrez dans le chapitre 12), il est important de comprendre comment les constructeurs se comportent dans des hiérarchies complexes combiné avec le polymorphisme. Cette compréhension vous aidera a éviter de désagréables plats de nouilles.
t t t

Order of constructor calls

t

Ordre d'appel des constructeurs

t t t
The order of constructor calls was briefly discussed in Chapter 4 and again in Chapter 6, but that was before polymorphism was introduced. t L'ordre d'appel des constructeurs a été brièvement discuté dans le chapitre 4 et également dans le chapitre 6, mais c'était avant l'introduction du polymorphisme.
t t t
A constructor for the base class is always called in the constructor for a derived class, chaining up the inheritance hierarchy so that a constructor for every base class is called. This makes sense because the constructor has a special job: to see that the object is built properly. A derived class has access to its own members only, and not to those of the base class (whose members are typically private). Only the base-class constructor has the proper knowledge and access to initialize its own elements. Therefore, it’s essential that all constructors get called, otherwise the entire object wouldn’t be constructed. That’s why the compiler enforces a constructor call for every portion of a derived class. It will silently call the default constructor if you don’t explicitly call a base-class constructor in the derived-class constructor body. If there is no default constructor, the compiler will complain. (In the case where a class has no constructors, the compiler will automatically synthesize a default constructor.) t Un constructeur de la classe de base est toujours appelé dans le constructeur d'une classe dérivée, en remontant la hiérarchie d'héritage de sorte qu'un constructeur pour chaque classe de base est appelé. Ceci semble normal car le travail du constructeur est précisément de construire correctement l'objet. Une classe dérivée a seulement accès à ses propres membres, et pas à ceux de la classe de base (dont les membres sont en général private). Seul le constructeur de la classe de base a la connaissance et l'accès appropriés pour initialiser ses propres éléments. Par conséquent, il est essentiel que tous les constructeurs soient appelés, sinon l'objet ne serait pas entièrement construit. C'est pourquoi le compilateur impose un appel de constructeur pour chaque partie d'une classe dérivée. Il appellera silencieusement le constructeur par défaut si vous n'appelez pas explicitement un constructeur de la classe de base dans le corps du constructeur de la classe dérivée. S' il n'y a aucun constructeur de défaut, le compilateur le réclamera (dans le cas où une classe n'a aucun constructeur, le compilateur générera automatiquement un constructeur par défaut).
t t t
Let’s take a look at an example that shows the effects of composition, inheritance, and polymorphism on the order of construction: t Prenons un exemple qui montre les effets de la composition, de l'héritage, et du polymorphisme sur l'ordre de construction :
t t t
//: c07:Sandwich.java
// Order of constructor calls.

class Meal {
  Meal() { System.out.println("Meal()"); }
}

class Bread {
  Bread() { System.out.println("Bread()"); }
}

class Cheese {
  Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println("Lunch()");}
}

class PortableLunch extends Lunch {
  PortableLunch() {
    System.out.println("PortableLunch()");
  }
}

class Sandwich extends PortableLunch {
  Bread b = new Bread();
  Cheese c = new Cheese();
  Lettuce l = new Lettuce();
  Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
  }
} ///:~
t
//: c07:Sandwich.java
// Ordre d'appel des constructeurs.
class Meal {
  Meal() { System.out.println("Meal()"); }
}

class Bread {
  Bread() { System.out.println("Bread()"); }
}

class Cheese {
  Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println("Lunch()");}
}

class PortableLunch extends Lunch {
  PortableLunch() {
    System.out.println("PortableLunch()");
  }
}

class Sandwich extends PortableLunch {
  Bread b = new Bread();
  Cheese c = new Cheese();
  Lettuce l = new Lettuce();
  Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
  }
} ///:~
t t t
This example creates a complex class out of other classes, and each class has a constructor that announces itself. The important class is Sandwich, which reflects three levels of inheritance (four, if you count the implicit inheritance from Object) and three member objects. When a Sandwich object is created in main( ), the output is: t Cet exemple utilise une classe complexe et d'autres classes, chaque classe a un constructeur qui s'annonce lui-même. La classe importante est Sandwich, qui est au troisième niveau d'héritage (quatre, si vous comptez l'héritage implicite de Object) et qui a trois objets membres. Quand un objet Sandwich est créé dans le main(), l'output est :
t t t
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
t
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
t t t
This means that the order of constructor calls for a complex object is as follows: t Ceci signifie que l'ordre d'appel des constructeurs pour un objet complexe est le suivant :
t t t
  1. The base-class constructor is called. This step is repeated recursively such that the root of the hierarchy is constructed first, followed by the next-derived class, etc., until the most-derived class is reached.
  2. Member initializers are called in the order of declaration.
  3. The body of the derived-class constructor is called.
t
  1. Le constructeur de la classe de base est appelé. Cette étape est répétée récursivement jusqu'à ce que la racine de la hiérarchie soit construite d'abord, suivie par la classe dérivée suivante, etc, jusqu'à atteindre la classe la plus dérivée.
  2. Les initialiseurs des membres sont appelés dans l'ordre de déclaration
  3. Le corps du constructeur de la classe dérivée est appelée.
t t t
The order of the constructor calls is important. When you inherit, you know all about the base class and can access any public and protected members of the base class. This means that you must be able to assume that all the members of the base class are valid when you’re in the derived class. In a normal method, construction has already taken place, so all the members of all parts of the object have been built. Inside the constructor, however, you must be able to assume that all members that you use have been built. The only way to guarantee this is for the base-class constructor to be called first. Then when you’re in the derived-class constructor, all the members you can access in the base class have been initialized. “Knowing that all members are valid” inside the constructor is also the reason that, whenever possible, you should initialize all member objects (that is, objects placed in the class using composition) at their point of definition in the class (e.g., b, c, and l in the example above). If you follow this practice, you will help ensure that all base class members and member objects of the current object have been initialized. Unfortunately, this doesn’t handle every case, as you will see in the next section. t L'ordre d'appel des constructeurs est important. Quand vous héritez, vous savez tout au sujet de la classe de base et pouvez accéder à tous les membres public et protected de la classe de base. Ceci signifie que vous devez pouvoir présumer que tous les membres de la classe de base sont valides quand vous êtes dans la classe dérivée. Dans une méthode normale, la construction a déjà eu lieu, ainsi tous les membres de toutes les parties de l'objet ont été construits. Dans le constructeur, cependant, vous devez pouvoir supposer que tous les membres que vous utilisez ont été construits. La seule manière de le garantir est d'appeler d'abord le constructeur de la classe de base. Ainsi, quand êtes dans le constructeur de la classe dérivée, tous les membres que vous pouvez accéder dans la classe de base ont été initialisés. Savoir que tous les membres sont valides à l'intérieur du constructeur est également la raison pour laquelle, autant que possible, vous devriez initialiser tous les objets membres (c'est à dire les objets mis dans la classe par composition) à leur point de définition dans la classe (par exemple, b, c, et l dans l'exemple ci-dessus). Si vous suivez cette recommandation, vous contribuerez à vous assurer que tous les membres de la classe de base et les objets membres de l'objet actuel aient été initialisés. Malheureusement, cela ne couvre pas tous les cas comme vous allez le voir dans le paragraphe suivant.
t t t

Inheritance and finalize( )

t

La méthode finalize() et l'héritage

t t t
When you use composition to create a new class, you never worry about finalizing the member objects of that class. Each member is an independent object, and thus is garbage collected and finalized regardless of whether it happens to be a member of your class. With inheritance, however, you must override finalize( ) in the derived class if you have any special cleanup that must happen as part of garbage collection. When you override finalize( ) in an inherited class, it’s important to remember to call the base-class version of finalize( ), since otherwise the base-class finalization will not happen. The following example proves this: t Quand vous utilisez la composition pour créer une nouvelle classe, vous ne vous préoccupez pas de l'achèvement des objets membres de cette classe. Chaque membre est un objet indépendant et traité par le garbage collector indépendamment du fait qu'il soit un membre de votre classe. Avec l'héritage, cependant, vous devez redéfinir finalize() dans la classe dérivée si un nettoyage spécial doit être effectué pendant la phase de garbage collection. Quand vous redéfinissez finalize() dans une classe fille, il est important de ne pas oublier d'appeler la version de finalize() de la classe de base, sinon l'achèvement de la classe de base ne se produira pas. L'exemple suivant le prouve :
t t t
//: c07:Frog.java
// Testing finalize with inheritance.

class DoBaseFinalization {
  public static boolean flag = false;
}

class Characteristic {
  String s;
  Characteristic(String c) {
    s = c;
    System.out.println(
      "Creating Characteristic " + s);
  }
  protected void finalize() {
    System.out.println(
      "finalizing Characteristic " + s);
  }
}

class LivingCreature {
  Characteristic p =
    new Characteristic("is alive");
  LivingCreature() {
    System.out.println("LivingCreature()");
  }
  protected void finalize() throws Throwable {
    System.out.println(
      "LivingCreature finalize");
    // Call base-class version LAST!
    if(DoBaseFinalization.flag)
      super.finalize();
  }
}

class Animal extends LivingCreature {
  Characteristic p =
    new Characteristic("has heart");
  Animal() {
    System.out.println("Animal()");
  }
  protected void finalize() throws Throwable {
    System.out.println("Animal finalize");
    if(DoBaseFinalization.flag)
      super.finalize();
  }
}

class Amphibian extends Animal {
  Characteristic p =
    new Characteristic("can live in water");
  Amphibian() {
    System.out.println("Amphibian()");
  }
  protected void finalize() throws Throwable {
    System.out.println("Amphibian finalize");
    if(DoBaseFinalization.flag)
      super.finalize();
  }
}

public class Frog extends Amphibian {
  Frog() {
    System.out.println("Frog()");
  }
  protected void finalize() throws Throwable {
    System.out.println("Frog finalize");
    if(DoBaseFinalization.flag)
      super.finalize();
  }
  public static void main(String[] args) {
    if(args.length != 0 &&
       args[0].equals("finalize"))
       DoBaseFinalization.flag = true;
    else
      System.out.println("Not finalizing bases");
    new Frog(); // Instantly becomes garbage
    System.out.println("Bye!");
    // Force finalizers to be called:
    System.gc();
  }
} ///:~
t
//: c07:Frog.java
// Test de la méthode finalize avec l'héritage.

class DoBaseFinalization {
  public static boolean flag = false;
}

class Characteristic {
  String s;
  Characteristic(String c) {
    s = c;
    System.out.println(
      "Creating Characteristic " + s);
  }
  protected void finalize() {
    System.out.println(
      "finalizing Characteristic " + s);
  }
}

class LivingCreature {
  Characteristic p =
    new Characteristic("is alive");
  LivingCreature() {
    System.out.println("LivingCreature()");
  }
  protected void finalize() {
    System.out.println(
      "LivingCreature finalize");
    // Appel de la version de la classe de base, à la fin!
    if(DoBaseFinalization.flag)
      try {
        super.finalize();
      } catch(Throwable t) {}
  }
}

class Animal extends LivingCreature {
  Characteristic p =
    new Characteristic("has heart");
  Animal() {
    System.out.println("Animal()");
  }
  protected void finalize() {
    System.out.println("Animal finalize");
    if(DoBaseFinalization.flag)
      try {
        super.finalize();
      } catch(Throwable t) {}
  }
}

class Amphibian extends Animal {
  Characteristic p =
    new Characteristic("can live in water");
  Amphibian() {
    System.out.println("Amphibian()");
  }
  protected void finalize() {
    System.out.println("Amphibian finalize");
    if(DoBaseFinalization.flag)
      try {
        super.finalize();
      } catch(Throwable t) {}
  }
}

public class Frog extends Amphibian {
  Frog() {
    System.out.println("Frog()");
  }
  protected void finalize() {
    System.out.println("Frog finalize");
    if(DoBaseFinalization.flag)
      try {
        super.finalize();
      } catch(Throwable t) {}
  }
  public static void main(String[] args) {
    if(args.length != 0 &&
       args[0].equals("finalize"))
       DoBaseFinalization.flag = true;
    else
          System.out.println("not finalizing bases");
    new Frog(); // Devient instantanément récupérable par le garbage collector
    System.out.println("bye!");
    // Force l'appel des finalisations:
    System.gc();
  }
} ///:~
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel