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 class DoBaseFinalization simply holds a flag that indicates to each class in the hierarchy whether to call super.finalize( ). This flag is set based on a command-line argument, so you can view the behavior with and without base-class finalization. t La classe DoBaseFinalization a simplement un indicateur qui contrôle l’appel de super.finalize() pour toutes les classes de la hiérarchie. Cet indicateur est positionné par un argument de l’appel en ligne, vous pouvez ainsi visualiser le comportement selon l’invocation ou non de la finalisation dans la classe de base.
t t t
Each class in the hierarchy also contains a member object of class Characteristic. You will see that regardless of whether the base class finalizers are called, the Characteristic member objects are always finalized. t Chaque classe dans la hiérarchie contient également un objet de la classe Characteristic. Vous constaterez que les objets de Characteristic sont toujours finalisés indépendamment de l’appel conditionné des finaliseurs de la classe de base.
t t t
Each overridden finalize( ) must have access to at least protected members since the finalize( ) method in class Object is protected and the compiler will not allow you to reduce the access during inheritance. (“Friendly” is less accessible than protected.) t Chaque méthode finalize() redéfinie doit au moins avoir accès aux membres protected puisque la méthode finalize() de la classe Object est protected et le que compilateur ne vous permettra pas de réduire l'accès pendant l'héritage (« Friendly » est moins accessible que protected).
t t t
In Frog.main( ), the DoBaseFinalization flag is configured and a single Frog object is created. Remember that garbage collection—and in particular finalization—might not happen for any particular object, so to enforce this, the call to System.gc( ) triggers garbage collection, and thus finalization. Without base-class finalization, the output is: t Dans Frog.main(), l'indicateur DoBaseFinalization est configuré et un seul objet Frog est créé. Rappelez-vous que la phase de garbage collection, et en particulier la finalisation, ne peut pas avoir lieu pour un objet particulier, ainsi pour la forcer, l'appel à System.gc() déclenche le garbage collector, et ainsi la finalisation. Sans finalisation de la classe de base, l'output est le suivant :
t t t
Not finalizing bases
Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
Bye!
Frog finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water
t
not finalizing bases
Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
bye!
Frog finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water
t t t
You can see that, indeed, no finalizers are called for the base classes of Frog (the member objects are finalized, as you would expect). But if you add the “finalize” argument on the command line, you get: t Vous pouvez constater qu'aucune finalisation n'est appelée pour les classes de base de Frog (les objets membres, eux, sont achevés, comme on s'y attendait). Mais si vous ajoutez l'argument « finalize » sur la ligne de commande, on obtient ce qui suit :
t t t
Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
bye!
Frog finalize
Amphibian finalize
Animal finalize
LivingCreature finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water
t
Creating Characteristic is alive
LivingCreature()
Creating Characteristic has heart
Animal()
Creating Characteristic can live in water
Amphibian()
Frog()
bye!
Frog finalize
Amphibian finalize
Animal finalize
LivingCreature finalize
finalizing Characteristic is alive
finalizing Characteristic has heart
finalizing Characteristic can live in water
t t t
Although the order the member objects are finalized is the same order that they are created, technically the order of finalization of objects is unspecified. With base classes, however, you have control over the order of finalization. The best order to use is the one that’s shown here, which is the reverse of the order of initialization. Following the form that’s used in C++ for destructors, you should perform the derived-class finalization first, then the base-class finalization. That’s because the derived-class finalization could call some methods in the base class that require that the base-class components are still alive, so you must not destroy them prematurely. t Bien que l'ordre de finalisation des objets membres soit le même que l'ordre de création, l'ordre de finalisation des objets est techniquement non spécifié. Cependant, vous avez le contrôle sur cet ordre pour les classes de base. Le meilleur ordre à suivre est celui qui est montré ici, et qui est l'ordre inverse de l'initialisation. Selon le modèle qui est utilisé pour des destructeurs en C++, vous devez d'abord exécuter la finalisation des classes dérivées, puis la finalisation de la classe de base. La raison est que la finalisation des classes dérivées pourrait appeler des méthodes de la classe de base qui exigent que les composants de la classe de base soient toujours vivants, donc vous ne devez pas les détruire prématurément.
t t t

Behavior of polymorphic methods inside constructors

t

Comportement des méthodes polymorphes dans les constructeursname="Index722">

t t t
The hierarchy of constructor calls brings up an interesting dilemma. What happens if you’re inside a constructor and you call a dynamically bound method of the object being constructed? Inside an ordinary method you can imagine what will happen—the dynamically bound call is resolved at run-time because the object cannot know whether it belongs to the class that the method is in or some class derived from it. For consistency, you might think this is what should happen inside constructors. t La hiérarchie d'appel des constructeurs pose un dilemme intéressant. Qu'arrive t-il si à l'intérieur d'un constructeur vous appelez une méthode dynamiquement attachée de l'objet en cours de construction? À l'intérieur d'une méthode ordinaire vous pouvez imaginer ce qui arriverait: l'appel dynamiquement attaché est résolu à l'exécution parce que l'objet ne peut pas savoir s'il appartient à la classe dans laquelle se trouve la méthode ou bien dans une classe dérivée. Par cohérence, vous pourriez penser que c'est ce qui doit arriver dans les constructeurs.
t t t
This is not exactly the case. If you call a dynamically bound method inside a constructor, the overridden definition for that method is used. However, the effect can be rather unexpected, and can conceal some difficult-to-find bugs. t Ce n'est pas ce qui se passe. Si vous appelez une méthode dynamiquement attachée à l'intérieur d'un constructeur, c'est la définition redéfinie de cette méthode est appelée. Cependant, l'effet peut être plutôt surprenant et peut cacher des bugs difficiles à trouver.
t t t
Conceptually, the constructor’s job is to bring the object into existence (which is hardly an ordinary feat). Inside any constructor, the entire object might be only partially formed—you can know only that the base-class objects have been initialized, but you cannot know which classes are inherited from you. A dynamically bound method call, however, reaches “outward” into the inheritance hierarchy. It calls a method in a derived class. If you do this inside a constructor, you call a method that might manipulate members that haven’t been initialized yet—a sure recipe for disaster. t Le travail du constructeur est conceptuellement d'amener l'objet à l'existence (qui est à peine un prouesse ordinaire). À l'intérieur de n'importe quel constructeur, l'objet entier pourrait être seulement partiellement formé - vous pouvez savoir seulement que les objets de la classe de base ont été initialisés, mais vous ne pouvez pas connaître les classes filles qui hérite de vous. Cependant, un appel de méthode dynamiquement attaché, atteint « extérieurement » la hiérarchie d'héritage. Il appelle une méthode dans une classe dérivée. Si vous faites ça à l'intérieur d'un constructeur, vous appelez une méthode qui pourrait manipuler des membres non encore initialisés - une recette très sûre pour le désastre.
t t t
You can see the problem in the following example: t Vous pouvez voir le problème dans l'exemple suivant :
t t t
//: c07:PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect.

abstract class Glyph {
  abstract void draw();
  Glyph() {
    System.out.println("Glyph() before draw()");
    draw();
    System.out.println("Glyph() after draw()");
  }
}

class RoundGlyph extends Glyph {
  int radius = 1;
  RoundGlyph(int r) {
    radius = r;
    System.out.println(
      "RoundGlyph.RoundGlyph(), radius = "
      + radius);
  }
  void draw() {
    System.out.println(
      "RoundGlyph.draw(), radius = " + radius);
  }
}

public class PolyConstructors {
  public static void main(String[] args) {
    new RoundGlyph(5);
  }
} ///:~
t
//: c07:PolyConstructors.java
// Constructeurs et polymorphisme ne conduisent
// pas ce à quoi que vous pourriez vous attendre.
abstract class Glyph {
  abstract void draw();
  Glyph() {
    System.out.println("Glyph() before draw()");
    draw();
    System.out.println("Glyph() after draw()");
  }
}

class RoundGlyph extends Glyph {
  int radius = 1;
  RoundGlyph(int r) {
    radius = r;
    System.out.println(
      "RoundGlyph.RoundGlyph(), radius = "
      + radius);
  }
  void draw() {
    System.out.println(
      "RoundGlyph.draw(), radius = " + radius);
  }
}

public class PolyConstructors {
  public static void main(String[] args) {
    new RoundGlyph(5);
  }
} ///:~
t t t
In Glyph, the draw( ) method is abstract, so it is designed to be overridden. Indeed, you are forced to override it in RoundGlyph. But the Glyph constructor calls this method, and the call ends up in RoundGlyph.draw( ), which would seem to be the intent. But look at the output: t Dans Glyph, la méthode dessiner [draw()] est abstraite; elle a donc été conçue pour être redéfinie. En effet, vous êtes forcés de la redéfinir dans RoundGlyph. Mais le constructeur de Glyph appelle cette méthode et l'appel aboutit à RoundGlyph.draw(), ce qui semble être l'intention. Mais regardez l'output :
t t t
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
t
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
t t t
When Glyph’s constructor calls draw( ), the value of radius isn’t even the default initial value 1. It’s 0. This would probably result in either a dot or nothing at all being drawn on the screen, and you’d be left staring, trying to figure out why the program won’t work. t Quand le constructeur de Glyph appelle draw(), le rayon [radius] n'a même pas encore la valeur initiale de 1, il vaut zéro. Le résultat serait probablement réduit à l'affichage d'un point ou même à rien du tout, avec vous, fixant un écran désespérément vide essayant de comprendre pourquoi le programme ne marche pas.
t t t
The order of initialization described in the previous section isn’t quite complete, and that’s the key to solving the mystery. The actual process of initialization is: t L'ordre de l'initialisation décrit dans la section précédente n'est pas complètement exhaustif, et c'est la clé qui va résoudre le mystère. La procédure d'initialisation est la suivante :
t t t
  1. The storage allocated for the object is initialized to binary zero before anything else happens.
  2. The base-class constructors are called as described previously. At this point, the overridden draw( ) method is called (yes, before the RoundGlyph constructor is called), which discovers a radius value of zero, due to step 1.
  3. Member initializers are called in the order of declaration.
  4. The body of the derived-class constructor is called.
t
  1. La zone allouée à l'objet est initialisée à zéro binaire avant tout.
  2. Les constructeurs des classes de base sont appelés comme décrit précédemment. Puis, la méthode draw() redéfinie est appelée (et oui, avant l'appel du constructeur de RoundGlyph), et utilise radius qui vaut zéro à cause de la première étape.
  3. Les initialiseurs des membres sont appelés dans l'ordre de déclaration.
  4. Le corps du constructeur de la classe dérivée est appelé
t t t
There’s an upside to this, which is that everything is at least initialized to zero (or whatever zero means for that particular data type) and not just left as garbage. This includes object references that are embedded inside a class via composition, which become null. So if you forget to initialize that reference you’ll get an exception at run-time. Everything else gets zero, which is usually a telltale value when looking at output. t Le bon coté est que tout est au moins initialisé au zéro (selon la signification de zéro pour un type de donnée particulier) et non laissé avec n'importe quelles valeurs. Cela inclut les références d'objet qui sont incorporés à l'intérieur d'une classe par composition, et qui passent à null. Ainsi si vous oubliez d'initialiser une référence vous obtiendrez une exception à l'exécution. Tout le reste est à zéro, qui est habituellement une valeur que l'on repère en examinant l'output.
t t t
On the other hand, you should be pretty horrified at the outcome of this program. You’ve done a perfectly logical thing, and yet the behavior is mysteriously wrong, with no complaints from the compiler. (C++ produces more rational behavior in this situation.) Bugs like this could easily be buried and take a long time to discover. t D'autre part, vous devez être assez horrifiés du résultat de ce programme. Vous avez fait une chose parfaitement logique et pourtant le comportement est mystérieusement faux, sans aucune manifestation du compilateur (C ++ a un comportement plus correct dans la même situation). Les bugs dans ce goût là peuvent facilement rester cachés et nécessiter pas mal de temps d'investigation.
t t t
As a result, a good guideline for constructors is, “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any methods.” The only safe methods to call inside a constructor are those that are final in the base class. (This also applies to private methods, which are automatically final.) These cannot be overridden and thus cannot produce this kind of surprise. t Il en résulte la recommandation suivante pour les constructeurs: « Faire le minimum pour mettre l'objet dans un bon état et si possible, ne pas appeler de méthodes. » Les seules méthodes qui sont appelables en toute sécurité à l'intérieur d'un constructeur sont celles qui sont finales dans la classe de base (même chose pour les méthodes privées, qui sont automatiquement finales.). Celles-ci ne peuvent être redéfinies et ne réservent donc pas de surprise.
t t t

Designing with inheritance

t

Concevoir avec l'héritage

t t t
Once you learn about polymorphism, it can seem that everything ought to be inherited because polymorphism is such a clever tool. This can burden your designs; in fact if you choose inheritance first when you’re using an existing class to make a new class, things can become needlessly complicated. t Après avoir vu le polymorphisme, c'est un instrument tellement astucieux qu'on dirait que tout doit être hérité. Ceci peut alourdir votre conception; en fait si vous faites le choix d'utiliser l'héritage d'entrée lorsque vous créez une nouvelle classe à partir d'une classe existante, cela peut devenir inutilement compliqué.
t t t
A better approach is to choose composition first, when it’s not obvious which one you should use. Composition does not force a design into an inheritance hierarchy. But composition is also more flexible since it’s possible to dynamically choose a type (and thus behavior) when using composition, whereas inheritance requires an exact type to be known at compile-time. The following example illustrates this: t Une meilleure approche est de choisir d'abord la composition, quand il ne vous semble pas évident de choisir entre les deux. La composition n'oblige pas à concevoir une hiérarchie d'héritage, mais elle est également plus flexible car il est alors possible de choisir dynamiquement un type (et son comportement), alors que l'héritage requiert un type exact déterminé au moment de la compilation. L'exemple suivant l'illustre :
t t t
//: c07:Transmogrify.java
// Dynamically changing the behavior of
// an object via composition.

abstract class Actor {
  abstract void act();
}

class HappyActor extends Actor {
  public void act() {
    System.out.println("HappyActor");
  }
}

class SadActor extends Actor {
  public void act() {
    System.out.println("SadActor");
  }
}

class Stage {
  Actor a = new HappyActor();
  void change() { a = new SadActor(); }
  void go() { a.act(); }
}

public class Transmogrify {
  public static void main(String[] args) {
    Stage s = new Stage();
    s.go(); // Prints "HappyActor"
    s.change();
    s.go(); // Prints "SadActor"
  }
} ///:~
t
//: c07:Transmogrify.java
// Changer dynamiquement le comportement
// d'un objet par la composition.
abstract class Actor {
  abstract void act();
}

class HappyActor extends Actor {
  public void act() {
    System.out.println("HappyActor");
  }
}

class SadActor extends Actor {
  public void act() {
    System.out.println("SadActor");
  }
}

class Stage {
  Actor a = new HappyActor();
  void change() { a = new SadActor(); }
  void go() { a.act(); }
}

public class Transmogrify {
  public static void main(String[] args) {
    Stage s = new Stage();
    s.go(); // Imprime "HappyActor"
    s.change();
    s.go(); // Imprime "SadActor"
  }
} ///:~
t t t
A Stage object contains a reference to an Actor, which is initialized to a HappyActor object. This means go( ) produces a particular behavior. But since a reference can be rebound to a different object at run-time, a reference for a SadActor object can be substituted in a and then the behavior produced by go( ) changes. Thus you gain dynamic flexibility at run-time. (This is also called the State Pattern. See Thinking in Patterns with Java, downloadable at www.BruceEckel.com.) In contrast, you can’t decide to inherit differently at run-time; that must be completely determined at compile-time. t Un objet Stage contient une référence vers un Actor, qui est initialisé par un objet HappyActor. Cela signifie que go() produit un comportement particulier. Mais puisqu'une référence peut être reliée à un objet différent à l'exécution, une référence à un objet SadActor peut être substituée dans a et alors le comportement produit par go() change. Ainsi vous gagnez en flexibilité dynamique à l'exécution (également appelé le State Pattern. Voir Thinking in Patterns with Java, téléchargeable sur www.BruceEckel.com.). Par contre, vous ne pouvez pas décider d'hériter différemment à l'exécution; cela doit être complètement déterminé à la compilation.
t t t
A general guideline is “Use inheritance to express differences in behavior, and fields to express variations in state.” In the above example, both are used: two different classes are inherited to express the difference in the act( ) method, and Stage uses composition to allow its state to be changed. In this case, that change in state happens to produce a change in behavior. t Voici une recommandation générale: « Utilisez l'héritage pour exprimer les différences de comportement, et les champs pour exprimer les variations d'état. » Dans l'exemple ci-dessus, les deux sont utilisés: deux classes différentes héritent pour exprimer la différence dans la méthode act(), et Stage utilise la composition pour permettre à son état d'être changé. Dans ce cas, ce changement d'état provoque un changement de comportement.
t t t

Pure inheritance vs. extension

t

Héritage pur contre extensionname="Index739">

t t t
When studying inheritance, it would seem that the cleanest way to create an inheritance hierarchy is to take the “pure” approach. That is, only methods that have been established in the base class or interface are to be overridden in the derived class, as seen in this diagram: t Lorsque l'on étudie l'héritage, il semblerait que la façon la plus propre de créer une hiérarchie d'héritage est de suivre l'approche « pure. » A savoir que seules les méthodes qui ont été établies dans la classe de base ou l'interface sont surchargeables dans la classe dérivée, comme le montre ce diagramme :
t t t
t Image
t t t
This can be termed a pure “is-a” relationship because the interface of a class establishes what it is. Inheritance guarantees that any derived class will have the interface of the base class and nothing less. If you follow the above diagram, derived classes will also have no more than the base class interface. t Ceci peut se nommer une relation « est-un » pure car l'interface d'une classe établie ce qu'elle est. L'héritage garantie que toute classe dérivée aura l'interface de la classe de base et rien de moins. Si vous suivez le diagramme ci-dessus, les classes dérivées auront également pas plus que l'interface de la classe de base.
t t t
This can be thought of as pure substitution, because derived class objects can be perfectly substituted for the base class, and you never need to know any extra information about the subclasses when you’re using them: t Ceci peut être considéré comme une substitution pure, car les objets de classe dérivée peuvent être parfaitement substitués par la classe de base, et vous n'avez jamais besoin de connaître d' information supplémentaire sur les sous-classes quand vous les utilisez :
t t t
t Image
t t t
That is, the base class can receive any message you can send to the derived class because the two have exactly the same interface. All you need to do is upcast from the derived class and never look back to see what exact type of object you’re dealing with. Everything is handled through polymorphism. t Cela étant, la classe de base peut recevoir tout message que vous pouvez envoyer à la classe dérivée car les deux ont exactement la même interface. Tout ce que vous avez besoin de faire est d'utiliser l'upcast à partir de la classe dérivée et de ne jamais regarder en arrière pour voir quel type exact d'objet vous manipulez.
t t t
When you see it this way, it seems like a pure “is-a” relationship is the only sensible way to do things, and any other design indicates muddled thinking and is by definition broken. This too is a trap. As soon as you start thinking this way, you’ll turn around and discover that extending the interface (which, unfortunately, the keyword extends seems to encourage) is the perfect solution to a particular problem. This could be termed an “is-like-a” relationship because the derived class is like the base class—it has the same fundamental interface—but it has other features that require additional methods to implement: t En la considérant de cette manière, une relation pure « est-un » semble la seule façon sensée de pratiquer, et toute autre conception dénote une réflexion embrouillée et est par définition hachée. Ceci aussi est un piège. Dès que vous commencez à penser de cette manière, vous allez tourner en rond et découvrir qu'étendre l'interface (ce que, malencontreusement, le mot clé extends semble encourager) est la solution parfaite à un problème particulier. Ceci pourrait être qualifié de relation « est-comme-un » car la classe dérivée est comme la classe de base, elle a la même interface fondamentale mais elle a d'autres éléments qui nécessitent d'implémenter des méthodes additionnelles :
t t t
t Image
t t t
While this is also a useful and sensible approach (depending on the situation) it has a drawback. The extended part of the interface in the derived class is not available from the base class, so once you upcast you can’t call the new methods: t Mais si cette approche est aussi utile et sensée (selon la situation) elle a un inconvénient. La partie étendue de l'interface de la classe dérivée n'est pas accessible à partir de la classe de base, donc une fois que vous avez utilisé l'upcast vous ne pouvez pas invoquer les nouvelles méthodes :
t t t
t Image
t t t
If you’re not upcasting in this case, it won’t bother you, but often you’ll get into a situation in which you need to rediscover the exact type of the object so you can access the extended methods of that type. The following section shows how this is done. t Si vous n'upcastez pas dans ce cas, cela ne va pas vous incommoder, mais vous serez souvent dans une situation où vous aurez besoin de retrouver le type exact de l'objet afin de pouvoir accéder aux méthodes étendues de ce type. La section suivante montre comment cela se passe.
t t t

Downcasting and run-time type identification

t

Downcasting et identification du type à l'exécution

t t t
Since you lose the specific type information via an upcast (moving up the inheritance hierarchy), it makes sense that to retrieve the type information—that is, to move back down the inheritance hierarchy—you use a downcast. However, you know an upcast is always safe; the base class cannot have a bigger interface than the derived class, therefore every message you send through the base class interface is guaranteed to be accepted. But with a downcast, you don’t really know that a shape (for example) is actually a circle. It could instead be a triangle or square or some other type. t Puisque vous avez perdu l'information du type spécifique par un upcast (en remontant la hiérarchie d'héritage), il est logique de retrouver le type en redescendant la hiérarchie d'héritage par un downcast. Cependant, vous savez qu'un upcast est toujours sûr; la classe de base ne pouvant pas avoir une interface plus grande que la classe dérivée, ainsi tout message que vous envoyez par l'interface de la classe de base a la garantie d'être accepté. Mais avec un downcast, vous ne savez pas vraiment qu'une forme (par exemple) est en réalité un cercle. Cela pourrait plutôt être un triangle ou un carré ou quelque chose d'un autre type.
t t t
t Image
t t t
To solve this problem there must be some way to guarantee that a downcast is correct, so you won’t accidentally cast to the wrong type and then send a message that the object can’t accept. This would be quite unsafe. t Pour résoudre ce problème il doit y avoir un moyen de garantir qu'un downcast est correct, ainsi vous n'allez pas effectuer un cast accidentel vers le mauvais type et ensuite envoyer un message que l'objet ne pourrait accepter. Ce serait assez imprudent.
t t t
In some languages (like C++) you must perform a special operation in order to get a type-safe downcast, but in Java every cast is checked! So even though it looks like you’re just performing an ordinary parenthesized cast, at run-time this cast is checked to ensure that it is in fact the type you think it is. If it isn’t, you get a ClassCastException. This act of checking types at run-time is called run-time type identification (RTTI). The following example demonstrates the behavior of RTTI: t Dans certains langages (comme C++) vous devez effectuer une opération spéciale afin d'avoir un cast ascendant sûr, mais en Java tout cast est vérifié! Donc même si il semble que vous faites juste un cast explicite ordinaire, lors de l'exécution ce cast est vérifié pour assurer qu'en fait il s'agit bien du type auquel vous vous attendez. Si il ne l'est pas, vous récupérez une ClassCastException. Cette action de vérifier les types au moment de l'exécution est appelé run-time type identification (RTTI). L'exemple suivant montre le comportement de la RTTI :
t t t
//: c07:RTTI.java
// Downcasting & Run-time Type
// Identification (RTTI).
import java.util.*;

class Useful {
  public void f() {}
  public void g() {}
}

class MoreUseful extends Useful {
  public void f() {}
  public void g() {}
  public void u() {}
  public void v() {}
  public void w() {}
}

public class RTTI {
  public static void main(String[] args) {
    Useful[] x = {
      new Useful(),
      new MoreUseful()
    };
    x[0].f();
    x[1].g();
    // Compile-time: method not found in Useful:
    //! x[1].u();
    ((MoreUseful)x[1]).u(); // Downcast/RTTI
    ((MoreUseful)x[0]).u(); // Exception thrown
  }
} ///:~
t
//: c07:RTTI.java
// Downcasting & Run-time Type
// Identification (RTTI).
import java.util.*;

class Useful {
  public void f() {}
  public void g() {}
}

class MoreUseful extends Useful {
  public void f() {}
  public void g() {}
  public void u() {}
  public void v() {}
  public void w() {}
}

public class RTTI {
  public static void main(String[] args) {
    Useful[] x = {
      new Useful(),
      new MoreUseful()
    };
    x[0].f();
    x[1].g();
    // Compilation: méthode non trouvée dans Useful:
    //! x[1].u();
    ((MoreUseful)x[1]).u(); // Downcast/RTTI
    ((MoreUseful)x[0]).u(); // Exception envoyée
  }
} ///:~
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel