t
t
t
t
t t   12) Identification dynamique de type
tttt
t
carrea) Préface carreb) Avant-propos carre1) Introduction sur les &laqo; objets » carre2) Tout est &laqo; objet » carre3) Contrôle du flux du programme carre4) Initialization & Cleanup carre5) Cacher l'implémentation carre6) Réutiliser les classes carre7) Polymorphisme carre8) Interfaces & classes internes carre9) Stockage des objets carre10) Error Handling with Exceptions carre11) Le système d’E/S de Java 12) Identification dynamique de type carre13) Création de fenêtres & d'Applets carre14) Les &laqo; Threads » multiples carre15) Informatique distribuée carreA) Passage et retour d'objets carreB) L'Interface Java Natif (JNI) carreC) Conseils pour une programation stylée en Java carreD) Resources
Texte original t Traducteur : Daniel Le Berre
t
t
    
Ce chapitre contient 3 pages
1 2 3
\\\
t t t
t t t
t
05.07.01 - version 2.2 [Armel] :
- Ajout des tags de séparation des pages pour le site.
07.05.2001 - Version 2.1 :
- Mise en forme html (armel).
27.01.2001 - Version 2.0 :
- Problèmes :
Class literal ? Regular classes ? Reflection ? Profiler ?
Traducteur :
- Daniel Le Berre
Texte original :
-Thinking in Java, 2nd edition, Revision 10
© 2000 by Bruce Eckel
t t t
12: Run-time Type Identification t 12: Identification dynamique de type
t t t
The idea of run-time type identification (RTTI) seems fairly simple at first: it lets you find the exact type of an object when you only have a reference to the base type.
t Le principe de l'identification dynamique de type (Run-Time Type Identification, RTTI) semble très simple à première vue : connaître le type exact d'un objet à partir d'une simple référence sur un type de base.
t t t
However, the need for RTTI uncovers a whole plethora of interesting (and often perplexing) OO design issues, and raises fundamental questions of how you should structure your programs.
t Néanmoins, le besoin de RTTI dévoile une pléthore de problèmes intéressants (et souvent complexes)  en conception orientée objet, et renforce la question fondamentale de comment structurer ses programmes.
t t t
This chapter looks at the ways that Java allows you to discover information about objects and classes at run-time. This takes two forms: “traditional” RTTI, which assumes that you have all the types available at compile-time and run-time, and the “reflection” mechanism, which allows you to discover class information solely at run-time. The “traditional” RTTI will be covered first, followed by a discussion of reflection.
t Ce chapitre indique de quelle manière Java permet de découvrir dynamiquement des informations sur les objets et les classes. On le retrouve sous deux formes : le RTTI « classique », qui suppose que tous les types sont disponibles à la compilation et à l'exécution, et le mécanisme de « réflexion », qui permet de découvrir des informations sur les classes uniquement à l'exécution. Le RTTI « classique » sera traité en premier, suivi par une discussion sur la réflexion.
t t t

The need for RTTI

t

Le besoin de RTTI

t t t
Consider the now familiar example of a class hierarchy that uses polymorphism. The generic type is the base class Shape, and the specific derived types are Circle, Square, and Triangle:
t Revenons à notre exemple d'une hiérarchie de classes utilisant le polymorphisme. Le type générique est la classe de base Forme, et les types spécifiques dérivés sont Cercle, Carre et Triangle :
t t t

t Image
t t t
This is a typical class hierarchy diagram, with the base class at the top and the derived classes growing downward. The normal goal in object-oriented programming is for the bulk of your code to manipulate references to the base type (Shape, in this case), so if you decide to extend the program by adding a new class (Rhomboid, derived from Shape, for example), the bulk of the code is not affected. In this example, the dynamically bound method in the Shape interface is draw( ), so the intent is for the client programmer to call draw( ) through a generic Shape reference. draw( ) is overridden in all of the derived classes, and because it is a dynamically bound method, the proper behavior will occur even though it is called through a generic Shape reference. That’s polymorphism.
t C'est un diagramme de classe hiérarchique classique, avec la classe de base en haut et les classes dérivées qui en découlent. Le but usuel de la programmation orientée objet est de manipuler dans la majorité du code des références sur le type de base (Forme, ici), tel que si vous décidez de créer une nouvelle classe (Rhomboïde, dérivée de Forme, par exemple), ce code restera inchangé. Dans notre exemple, la méthode liée dynamiquement dans l'interface Forme est draw(), ceci dans le but que le programmeur appelle draw() à partir d'une référence sur un objet de type Forme. draw() est redéfinie dans toutes les classes dérivées, et parce que cette méthode est liée dynamiquement, le comportement attendu arrivera même si l'appel se fait à partir d'une référence générique sur Forme. C'est ce que l'on appelle le polymorphisme.
t t t
Thus, you generally create a specific object (Circle, Square, or Triangle), upcast it to a Shape (forgetting the specific type of the object), and use that anonymous Shape reference in the rest of the program.
t Ainsi, on peut créer un objet spécifique (Cercle, Carre ou Triangle), le transtyper à Forme (oubliant le type spécifique de l'objet), et utiliser une référence anonyme à Forme dans le reste du programme.
t t t
As a brief review of polymorphism and upcasting, you might code the above example as follows:
t Pour avoir un bref aperçu du polymorphisme et du transtypage ascendant (upcast), vous pouvez coder l'exemple ci-dessous :
t t t
//: c12:Shapes.java import java.util.*; class Shape { void draw() { System.out.println(this + ".draw()"); } } class Circle extends Shape { public String toString() { return "Circle"; } } class Square extends Shape { public String toString() { return "Square"; } } class Triangle extends Shape { public String toString() { return "Triangle"; } } public class Shapes { public static void main(String[] args) { ArrayList s = new ArrayList(); s.add(new Circle()); s.add(new Square()); s.add(new Triangle()); Iterator e = s.iterator(); while(e.hasNext()) ((Shape)e.next()).draw(); } } ///:~ t
//: c12:Formes.java
import java.util.*;

class Forme {
  void draw() {
    System.out.println(this + ".draw()");
  }
}

class Cercle extends Forme {
  public String toString() { return "Cercle"; }
}

class Carre extends Forme {
  public String toString() { return "Carre"; }
}

class Triangle extends Forme {
  public String toString() { return "Triangle"; }
}

public class Formes {
  public static void main(String[] args) {
    ArrayList s = new ArrayList();
    s.add(new Cercle());
    s.add(new Carre());
    s.add(new Triangle());
    Iterator e = s.iterator();
    while(e.hasNext())
      ((Shape)e.next()).draw();
  }
} ///:~
t t t
The base class contains a draw( ) method that indirectly uses toString( ) to print an identifier for the class by passing this to System.out.println( ). If that function sees an object, it automatically calls the toString( ) method to produce a String representation.
t La classe de base contient une méthode draw() qui utilise indirectement toString() pour afficher un identifiant de la classe en utilisant this en paramètre de System.out.println(). Si cette fonction rencontre un objet, elle appelle automatiquement la méthode toString() de cet objet pour en avoir une représentation sous forme de chaîne de caractères.
t t t
Each of the derived classes overrides the toString( ) method (from Object) so that draw( ) ends up printing something different in each case. In main( ), specific types of Shape are created and then added to an ArrayList. This is the point at which the upcast occurs because the ArrayList holds only Objects. Since everything in Java (with the exception of primitives) is an Object, an ArrayList can also hold Shape objects. But during an upcast to Object, it also loses any specific information, including the fact that the objects are Shapes. To the ArrayList, they are just Objects.
t Chacune des classes dérivées redéfinit la méthode toString() (de la classe Object) pour que draw() affiche quelque chose de différent dans chaque cas. Dans main(), des types spécifiques de Forme sont créés et ajoutés dans un ArrayList. C'est à ce niveau que le transtypage ascendant intervient car un ArrayList contient uniquement des Objects. Comme tout en Java (à l'exception des types primitifs) est Object, un ArrayList peut aussi contenir des Formes. Mais lors du transtypage en Object, il perd toutes les informations spécifiques des objets, par exemple que ce sont des Formes. Pour le ArrayList, ce sont juste des Objects.
t t t
At the point you fetch an element out of the ArrayList with next( ), things get a little busy. Since ArrayList holds only Objects, next( ) naturally produces an Object reference. But we know it’s really a Shape reference, and we want to send Shape messages to that object. So a cast to Shape is necessary using the traditional “(Shape)” cast. This is the most basic form of RTTI, since in Java all casts are checked at run-time for correctness. That’s exactly what RTTI means: at run-time, the type of an object is identified.
t Lorsqu'on récupère ensuite un élément de l'ArrayList avec la méthode next(), les choses se corsent un peu. Comme un ArrayList contient uniquement des Objects, next() va naturellement renvoyer une référence sur un Object. Mais nous savons que c'est en réalité une référence sur une Forme, et  nous désirons envoyer des messages de Forme à cet objet. Donc un transtypage en Forme est nécessaire en utilisant le transtypage habituel « (Forme) ». C'est la forme la plus simple de RTTI, puisqu'en Java l'exactitude de tous les typages est vérifiée à l'exécution. C'est exactement ce que signifie RTTI : à l'exécution, le type de tout objet est connu.
t t t
In this case, the RTTI cast is only partial: the Object is cast to a Shape, and not all the way to a Circle, Square, or Triangle. That’s because the only thing we know at this point is that the ArrayList is full of Shapes. At compile-time, this is enforced only by your own self-imposed rules, but at run-time the cast ensures it.
t Dans notre cas, le RTTI est seulement partiel : l'Object est transtypé en Forme, mais pas en Cercle, Carre ou Triangle. Ceci parce que la seule chose que nous savons à ce moment là est que l'ArrayList est rempli de Formes. A la compilation, ceci est garanti uniquement par vos propres choix (ndt : le compilateur vous fait confiance), tandis qu'à l'exécution le transtypage est effectivement vérifié.
t t t
Now polymorphism takes over and the exact method that’s called for the Shape is determined by whether the reference is for a Circle, Square, or Triangle. And in general, this is how it should be; you want the bulk of your code to know as little as possible about specific types of objects, and to just deal with the general representation of a family of objects (in this case, Shape). As a result, your code will be easier to write, read, and maintain, and your designs will be easier to implement, understand, and change. So polymorphism is the general goal in object-oriented programming.
t Maintenant le polymorphisme s'applique et la méthode exacte qui a été appelée pour une Forme est déterminée  selon que la référence est de type Cercle, Carre ou Triangle. Et en général, c'est comme cela qu'il faut faire ; on veut que la plus grosse partie du code ignore autant que possible le type spécifique des objets, et manipule une représentation générale de cette famille d'objets (dans notre cas, Forme). Il en résulte un code plus facile à écrire, lire et maintenir, et vos conceptions seront plus faciles à implémenter, comprendre et modifier. Le polymorphisme est donc un but général en programmation orientée objet.
t t t
But what if you have a special programming problem that’s easiest to solve if you know the exact type of a generic reference? For example, suppose you want to allow your users to highlight all the shapes of any particular type by turning them purple. This way, they can find all the triangles on the screen by highlighting them. This is what RTTI accomplishes: you can ask a Shape reference the exact type that it’s referring to.
t Mais que faire si vous avez un problème de programmation qui peut se résoudre facilement si vous connaissez le type exact de la référence générique que vous manipulez ? Par exemple, supposons que vous désiriez permettre à vos utilisateurs de colorier toutes les formes d'un type particulier en violet. De cette manière, ils peuvent retrouver tous les triangles à l'écran en les coloriant. C'est ce que fait le RTTI : on peut demander à une référence sur une Forme le type exact de l'objet référencé.
t t t

The Class object

t

L'objet Class

t t t
To understand how RTTI works in Java, you must first know how type information is represented at run-time. This is accomplished through a special kind of object called the Class object, which contains information about the class. (This is sometimes called a meta-class.) In fact, the Class object is used to create all of the “regular” objects of your class.
t Pour comprendre comment marche le RTTI en Java, il faut d'abord savoir comment est représentée l'information sur le type durant l'exécution. C'est le rôle d'un objet spécifique appelé l'objet Class, qui contient toutes les informations relative à la classe (on l'appelle parfois meta-class). En fait, l'objet Class est utilisé pour créer tous les objets « habituels » d'une classe.
t t t
There’s a Class object for each class that is part of your program. That is, each time you write and compile a new class, a single Class object is also created (and stored, appropriately enough, in an identically named .class file). At run-time, when you want to make an object of that class, the Java Virtual Machine (JVM) that’s executing your program first checks to see if the Class object for that type is loaded. If not, the JVM loads it by finding the .class file with that name. Thus, a Java program isn’t completely loaded before it begins, which is different from many traditional languages.
t Il y a un objet Class pour chacune des classes d'un programme. Ainsi, à chaque fois qu'une classe est écrite et compilée, un unique objet de type Class est aussi créé (et rangé, le plus souvent, dans un fichier .class du même nom). Durant l'exécution, lorsqu'un nouvel objet de cette classe doit être créé, la MachineVirtuelle Java (Java Virtual Machine, JVM) qui exécute le programme vérifie d'abord si l'objet Class associé est déjà chargé. Si non, la JVM le charge en cherchant un fichier .class du même nom. Ainsi un programme Java n'est pas totalement chargé en mémoire lorsqu'il démarre, contrairement à beaucoup de langages classiques.
t t t
Once the Class object for that type is in memory, it is used to create all objects of that type.
t Une fois que l'objet Class est en mémoire, il est utilisé pour créer tous les objets de ce type.
t t t
If this seems shadowy or if you don’t really believe it, here’s a demonstration program to prove it:
t Si cela ne vous semble pas clair ou si vous ne le croyez pas, voici un programme pour le prouver :
t t t
//: c12:SweetShop.java // Examination of the way the class loader works. class Candy { static { System.out.println("Loading Candy"); } } class Gum { static { System.out.println("Loading Gum"); } } class Cookie { static { System.out.println("Loading Cookie"); } } public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { e.printStackTrace(System.err); } System.out.println( "After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); } } ///:~ t
//: c12:Confiseur.java
// Étude du fonctionnement du chargeur de classes.

class Bonbon {
  static {
    System.out.println("Charge Bonbon");
  }
}

class Gomme {
  static {
    System.out.println("Charge Gomme");
  }
}

class Biscuit {
  static {
    System.out.println("Charge Biscuit");
  }
}

public class Confiseur {
  public static void main(String[] args) {
    System.out.println("Début méthode main");
    new Bonbon();
    System.out.println("Après création Gomme");
    try {
      Class.forName("Gomme");
    } catch(ClassNotFoundException e) {
      e.printStackTrace(System.err);
    }
    System.out.println(
      "Après Class.forName(\"Gomme\")");
    new Biscuit();
    System.out.println("Après création Biscuit");
  }
} ///:~
t t t
Each of the classes Candy, Gum, and Cookie have a static clause that is executed as the class is loaded for the first time. Information will be printed to tell you when loading occurs for that class. In main( ), the object creations are spread out between print statements to help detect the time of loading.
t Chacune des classes Bonbon, Gomme et Biscuit a une clause static qui est exécutée lorsque la classe est chargée la première fois. L'information qui est affichée vous permet de savoir quand cette classe est chargée. Dans la méthode main(), la création des objets est dispersée entre des opérations d'affichages pour faciliter la détection du moment du chargement.
t t t
A particularly interesting line is:
t Une ligne particulièrement intéressante est :
t t t
Class.forName("Gum"); t
Class.forName("Gomme");
t t t
This method is a static member of Class (to which all Class objects belong). A Class object is like any other object and so you can get and manipulate a reference to it. (That’s what the loader does.) One of the ways to get a reference to the Class object is forName( ), which takes a String containing the textual name (watch the spelling and capitalization!) of the particular class you want a reference for. It returns a Class reference.
t Cette méthode est une méthode static de Class (qui appartient à tous les objets Class). Un objet Class est comme tous les autres objets, il est donc possible d'obtenir sa référence et de la manipuler (c'est ce que fait le chargeur de classes). Un des moyens d'obtenir une référence sur un objet Class est la méthode forName(), qui prend en paramètre une chaîne de caractères contenant le nom (attention à l'orthographe et aux majuscules !) de la classe dont vous voulez la référence. Elle retourne une référence sur un objet Class.
t t t
The output of this program for one JVM is:
t Le résultat de ce programme pour une JVM est :
t t t
inside main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie t
Début méthode main
Charge Bonbon
Après création Bonbon
Charge Gomme
Après Class.forName("Gomme")
Charge Biscuit
Après création Biscuit
t t t
You can see that each Class object is loaded only when it’s needed, and the static initialization is performed upon class loading.
t On peut noter que chaque objet Class est chargé uniquement lorsque c'est nécessaire, et que l'initialisation static est effectuée au chargement de la classe.
t t t

Class literals

t

Les littéraux Class

t t t
Java provides a second way to produce the reference to the Class object, using a class literal. In the above program this would look like:
t Java fournit une deuxième manière d'obtenir une référence sur un objet de type Class, en utilisant le littéral class. Dans le programme précédent, on aurait par exemple :
t t t
Gum.class; t
Gomme.class;
t t t
which is not only simpler, but also safer since it’s checked at compile-time. Because it eliminates the method call, it’s also more efficient.
t ce qui n'est pas seulement plus simple, mais aussi plus sûr puisque vérifié à la compilation. Comme elle ne nécessite pas d'appel à une méthode, elle est aussi plus efficace.
t t t
Class literals work with regular classes as well as interfaces, arrays, and primitive types. In addition, there’s a standard field called TYPE that exists for each of the primitive wrapper classes. The TYPE field produces a reference to the Class object for the associated primitive type, such that:
t Les littéraux Class sont utilisables sur les classes habituelles ainsi que sur les interfaces, les tableaux et les types primitifs. De plus, il y a un attribut standard appelé TYPE qui existe pour chacune des classes englobant des types primitifs. L'attribut TYPE produit  une référence à l'objet Class associé au type primitif, tel que :
t t t
... is equivalent to ...
boolean.class
Boolean.TYPE
char.class
Character.TYPE
byte.class
Byte.TYPE
short.class
Short.TYPE
int.class
Integer.TYPE
long.class
Long.TYPE
float.class
Float.TYPE
double.class
Double.TYPE
void.class
Void.TYPE
t
... est équivalent à ...
boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE
t t t
My preference is to use the “.class” versions if you can, since they’re more consistent with regular classes.
t Ma préférence va à l'utilisation des « .class » si possible, car cela est plus consistant avec les classes habituelles.
t t t

Checking before a cast

t

Vérifier avant de transtyper

t t t
So far, you’ve seen RTTI forms including:
t Jusqu'à présent, nous avons vu différentes utilisations  de RTTI dont :
t t t
  1. The classic cast; e.g., “(Shape),” which uses RTTI to make sure the cast is correct and throws a ClassCastException if you’ve performed a bad cast.
  2. The Class object representing the type of your object. The Class object can be queried for useful run-time information.
t
  1. Le transtypage classique ; i.e. « (Forme) », qui utilise RTTI pour être sûr que le transtypage est correct et lancer une ClassCastException si un mauvais transtypage est effectué.
  2. L'objet Class qui représente le type d'un objet. L'objet Class peut être interrogé afin d'obtenir des informations utiles durant l'exécution.
t t t
In C++, the classic cast “(Shape)” does not perform RTTI. It simply tells the compiler to treat the object as the new type. In Java, which does perform the type check, this cast is often called a “type safe downcast.” The reason for the term “downcast” is the historical arrangement of the class hierarchy diagram. If casting a Circle to a Shape is an upcast, then casting a Shape to a Circle is a downcast. However, you know a Circle is also a Shape, and the compiler freely allows an upcast assignment, but you don’t know that a Shape is necessarily a Circle, so the compiler doesn’t allow you to perform a downcast assignment without using an explicit cast.
t En C++, le transtypage classique « (Forme) » n'effectue pas de RTTI. Il indique seulement au compilateur de traiter l'objet avec le nouveau type. En Java, qui effectue cette vérification de type, ce transtypage est souvent appelé “transtypage descendant sain”. La raison du terme « descendant » est liée à l'historique de la représentation des diagrammes de hiérarchie de classes. Si transtyper un Cercle en une Forme est un transtypage ascendant, alors transtyper une Forme en un Cercle est un transtypage descendant. Néanmoins, on sait que tout Cercle est aussi une Forme, et le compilateur nous laisse donc librement effectuer un transtypage descendant ; par contre toute Forme n'est pas nécessairement un Cercle, le compilateur ne permet donc pas de faire un transtypage descendant sans utiliser un transtypage explicite.
t t t
There’s a third form of RTTI in Java. This is the keyword instanceof that tells you if an object is an instance of a particular type. It returns a boolean so you use it in the form of a question, like this:
t Il existe une troisième forme de RTTI en Java. C'est le mot clef instanceof qui vous indique si un objet est d'un type particulier. Il retourne un boolean afin d'être utilisé sous la forme d'une question, telle que :
t t t
if(x instanceof Dog) ((Dog)x).bark(); t
if(x instanceof Chien)
  ((Chien)x).aboyer();
t t t
The above if statement checks to see if the object x belongs to the class Dog before casting x to a Dog. It’s important to use instanceof before a downcast when you don’t have other information that tells you the type of the object; otherwise you’ll end up with a ClassCastException.
t L'expression ci-dessus vérifie si un objet x appartient à la classe Chien avant de transtyper x en Chien. Il est important d'utiliser instanceof avant un transtypage descendant lorsque vous n'avez pas d'autres informations vous indiquant le type de l'objet, sinon vous risquez d'obtenir une ClassCastException.
t t t
Ordinarily, you might be hunting for one type (triangles to turn purple, for example), but you can easily tally all of the objects using instanceof. Suppose you have a family of Pet classes:
t Le plus souvent, vous rechercherez un type d'objets (les triangles à peindre en violet par exemple), mais vous pouvez aisément identifier tous les objets en utilisant instanceof. Supposons que vous ayez une famille de classes d'animaux de compagnie (Pet) :
t t t
//: c12:Pets.java class Pet {} class Dog extends Pet {} class Pug extends Dog {} class Cat extends Pet {} class Rodent extends Pet {} class Gerbil extends Rodent {} class Hamster extends Rodent {} class Counter { int i; } ///:~ t
//: c12:Pets.java
class Pet {}
class Chien extends Pet {}
class Carlin extends Chien {}
class Chat extends Pet {}
class Rongeur extends Pet {}
class Gerbil extends Rongeur {}
class Hamster extends Rongeur {}

class Counter { int i; } ///:~
t t t
The Counter class is used to keep track of the number of any particular type of Pet. You could think of it as an Integer that can be modified.
t La classe Counter est utilisée pour compter le nombre d'animaux de compagnie de chaque type. On peut le voir comme un objet Integer que l'on peut modifier.
t t t
Using instanceof, all the pets can be counted:
t En utilisant instanceof, tous les animaux peuvent être comptés :
t t t
//: c12:PetCount.java // Using instanceof. import java.util.*; public class PetCount { static String[] typenames = { "Pet", "Dog", "Pug", "Cat", "Rodent", "Gerbil", "Hamster", }; // Exceptions thrown out to console: public static void main(String[] args) throws Exception { ArrayList pets = new ArrayList(); try { Class[] petTypes = { Class.forName("Dog"), Class.forName("Pug"), Class.forName("Cat"), Class.forName("Rodent"), Class.forName("Gerbil"), Class.forName("Hamster"), }; for(int i = 0; i < 15; i++) pets.add( petTypes[ (int)(Math.random()*petTypes.length)] .newInstance()); } catch(InstantiationException e) { System.err.println("Cannot instantiate"); throw e; } catch(IllegalAccessException e) { System.err.println("Cannot access"); throw e; } catch(ClassNotFoundException e) { System.err.println("Cannot find class"); throw e; } HashMap h = new HashMap(); for(int i = 0; i < typenames.length; i++) h.put(typenames[i], new Counter()); for(int i = 0; i < pets.size(); i++) { Object o = pets.get(i); if(o instanceof Pet) ((Counter)h.get("Pet")).i++; if(o instanceof Dog) ((Counter)h.get("Dog")).i++; if(o instanceof Pug) ((Counter)h.get("Pug")).i++; if(o instanceof Cat) ((Counter)h.get("Cat")).i++; if(o instanceof Rodent) ((Counter)h.get("Rodent")).i++; if(o instanceof Gerbil) ((Counter)h.get("Gerbil")).i++; if(o instanceof Hamster) ((Counter)h.get("Hamster")).i++; } for(int i = 0; i < pets.size(); i++) System.out.println(pets.get(i).getClass()); for(int i = 0; i < typenames.length; i++) System.out.println( typenames[i] + " quantity: " + ((Counter)h.get(typenames[i])).i); } } ///:~ t
//: c12:PetCount.java
// Utiliser instanceof.
import java.util.*;

public class PetCount {
  static String[] typenames = {
    "Pet", "Chien", "Carlin", "Chat",
    "Rongeur", "Gerbil", "Hamster",
  };
  // Les exceptions remontent jusqu'à la console :
  public static void main(String[] args)
  throws Exception {
    ArrayList pets = new ArrayList();
    try {
      Class[] petTypes = {
        Class.forName("Chien"),
        Class.forName("Carlin"),
        Class.forName("Chat"),
        Class.forName("Rongeur"),
        Class.forName("Gerbil"),
        Class.forName("Hamster"),
      };
      for(int i = 0; i < 15; i++)
        pets.add(
          petTypes[
            (int)(Math.random()*petTypes.length)]
            .newInstance());
    } catch(InstantiationException e) {
      System.err.println("Instantiation impossible");
      throw e;
    } catch(IllegalAccessException e) {
      System.err.println("Accès impossible");
      throw e;
    } catch(ClassNotFoundException e) {
      System.err.println("Classe non trouvée");
      throw e;
    }
    HashMap h = new HashMap();
    for(int i = 0; i < typenames.length; i++)
      h.put(typenames[i], new Counter());
    for(int i = 0; i < pets.size(); i++) {
      Object o = pets.get(i);
      if(o instanceof Pet)
        ((Counter)h.get("Pet")).i++;
      if(o instanceof Chien)
        ((Counter)h.get("Chien")).i++;
      if(o instanceof Carlin)
        ((Counter)h.get("Carlin")).i++;
      if(o instanceof Chat)
        ((Counter)h.get("Chat")).i++;
      if(o instanceof Rongeur)
        ((Counter)h.get("Rongeur")).i++;
      if(o instanceof Gerbil)
        ((Counter)h.get("Gerbil")).i++;
      if(o instanceof Hamster)
        ((Counter)h.get("Hamster")).i++;
    }
    for(int i = 0; i < pets.size(); i++)
      System.out.println(pets.get(i).getClass());
    for(int i = 0; i < typenames.length; i++)
      System.out.println(
        typenames[i] + " quantité : " +
        ((Counter)h.get(typenames[i])).i);
  }
} ///:~
t t t
t t t
t t
    
///
t t t
t
     
Sommaire Le site de Bruce Eckel