 |
 |
12) Identification dynamique de type |
|
 |
|
Texte original |
 |
Traducteur : Daniel LE BERRE |
|
 |
|
Ce chapitre contient 3 pages
1
2
3
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
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
|
 |
 |
 |
12: Run-time Type Identification
|
 |
12: Identification dynamique de type
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
The need for RTTI
|
 |
Le besoin de RTTI
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
|
 |
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
As a brief review of polymorphism and
upcasting, you might code the above example as
follows:
|
 |
Pour avoir un bref aperçu du polymorphisme et du transtypage ascendant
(upcast), vous pouvez coder l'exemple ci-dessous :
|
 |
 |
 |
//: 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(); } } ///:~
|
 |
//: 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(); } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
The Class object
|
 |
L'objet Class
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Once the Class object for that
type is in memory, it is used to create all objects of that
type.
|
 |
Une fois que l'objet Class est en mémoire, il est utilisé
pour créer tous les objets de ce type.
|
 |
 |
 |
If this seems shadowy or if you
don’t really believe it, here’s a demonstration program to prove
it:
|
 |
Si cela ne vous semble pas clair ou si vous ne le croyez pas, voici un
programme pour le prouver :
|
 |
 |
 |
//: 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"); } } ///:~
|
 |
//: 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"); } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
A particularly interesting line
is:
|
 |
Une ligne particulièrement intéressante est :
|
 |
 |
 |
Class.forName("Gum");
|
 |
Class.forName("Gomme");
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
The output of this program for one JVM is:
|
 |
Le résultat de ce programme pour une JVM est :
|
 |
 |
 |
inside main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie
|
 |
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
|
 |
 |
 |
You can see that each Class object
is loaded only when it’s needed, and the static initialization is
performed upon class loading.
|
 |
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.
|
 |
 |
 |
Class literals
|
 |
Les littéraux Class
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
Gum.class;
|
 |
Gomme.class;
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
... 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
|
|
 |
... 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
|
|
 |
 |
 |
My preference is to use the “.class” versions if you can, since they’re more consistent with regular classes.
|
 |
Ma préférence va à l'utilisation des « .class » si
possible, car cela est plus consistant avec les classes habituelles.
|
 |
 |
 |
Checking before a cast
|
 |
Vérifier avant de transtyper
|
 |
 |
 |
So far, you’ve seen RTTI forms including:
|
 |
Jusqu'à présent, nous avons vu différentes utilisations de RTTI dont
:
|
 |
 |
 |
- 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. - The
Class object representing the type of your object. The Class object can be queried for useful run-time information.
|
 |
- 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é.
- 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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
if(x instanceof Dog) ((Dog)x).bark();
|
 |
if(x instanceof Chien) ((Chien)x).aboyer();
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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) :
|
 |
 |
 |
//: 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; } ///:~
|
 |
//: 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; } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Using instanceof, all the pets can
be counted:
|
 |
En utilisant instanceof, tous les animaux peuvent être
comptés :
|
 |
 |
 |
//: 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); } } ///:~
|
 |
//: 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); } } ///:~
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |