|
|
12) Identification dynamique de type |
|
|
|
Texte original |
|
Traducteur :
Daniel Le Berre |
|
|
///
|
Ce chapitre contient
3 pages
1
2
3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Another compelling motivation for
discovering class information at run-time is to provide the ability to create
and execute objects on remote platforms across a network. This is called
Remote Method Invocation (RMI) and it allows a Java program to have
objects distributed across many machines. This distribution can happen for a
number of reasons: for example, perhaps you’re doing a
computation-intensive task and you want to break it up and put pieces on
machines that are idle in order to speed things up. In some situations you might
want to place code that handles particular types of tasks (e.g., “Business
Rules” in a multitier client/server architecture) on a particular machine,
so that machine becomes a common repository describing those actions and it can
be easily changed to affect everyone in the system. (This is an interesting
development, since the machine exists solely to make software changes easy!)
Along these lines, distributed computing also supports specialized hardware that
might be good at a particular task—matrix inversions, for
example—but inappropriate or too expensive for general purpose
programming.
|
|
Une autre motivation pour découvrir dynamiquement des informations sur une
classe est de permettre la création et l'exécution d'objets sur des plates forme distantes via un
réseau. Ceci est appelé Remote Method Invocation (RMI) et permet à un programme Java
d'avoir des objets répartis sur plusieurs machines. Cette répartition peut survenir pour diverses
raisons : par exemple, vous effectuez une tâche coûteuse en calcul et vous voulez la dissocier pour
utiliser certains morceaux sur des machines libres afin d'accélérer les choses. Dans certaines
situations vous aimeriez placer le code d'un certain type de tâches (par exemple « Business Rules »
dans une architecture client/serveur multitiers ??????) sur une machine particulière, afin
que celle-ci devienne le dépositaire unique de ces tâches, pouvant être facilement modifié pour
changer tout le système (c'est un développement intéressant puisque la machine existe uniquement
pour faciliter les changements logiciels !). Enfin, le calcul distribué supporte aussi des
architectures spécialisées qui peuvent être appropriées à des tâches spécifiques- l'inversion de
matrices, par exemple- mais inappropriées ou trop chères pour la programmation
classique.
|
|
|
|
The class Class (described
previously in this chapter) supports the concept of reflection, and
there’s an additional library, java.lang.reflect, with classes
Field,
Method, and
Constructor (each of
which implement the Member interface). Objects of these types are created
by the JVM at run-time to represent the corresponding member in the unknown
class. You can then use the Constructors to create new objects, the
get( ) and set( ) methods to read and modify the fields
associated with Field objects, and the invoke( ) method to
call a method associated with a Method object. In addition, you can call
the convenience methods getFields( ), getMethods( ),
getConstructors( ), etc., to return arrays of the objects
representing the fields, methods, and constructors. (You can find out more by
looking up the class Class in your online documentation.) Thus,
the class information for anonymous objects can be completely determined at
run-time, and nothing need be known at compile-time.
|
|
La classe Class (décrite précédemment dans ce chapitre)
supporte le concept de réflexion, et une bibliothèque additionnelle,
java.lang.reflect, contenant les
classes Field, Method, et Constructor (chacune implémentant l'interface
Member). Les objets de ce type sont créés dynamiquement par la JVM pour
représenter les membres correspondants d'une classe inconnue. On peut alors utiliser les
constructeurs pour créer de nouveaux objets, les méthodes get() et
set() pour lire et modifier les champs associés à des objets
Field, et la méthode invoke() pour appeler une méthode associée à
un objet Method. De plus, on peut utiliser les méthodes très pratiques
getFields(), getMethods(), getConstructors(),
etc. retournant un tableau représentant respectivement des champs, méthodes et constructeurs (pour
en savoir plus, jetez un oeil à la documentation en ligne de la classe Class).
Ainsi, l'information sur la classe d'objets inconnus peut être totalement déterminée dynamiquement,
sans rien en savoir à la compilation.
|
|
|
|
It’s important to realize that
there’s nothing magic about reflection. When you’re using reflection
to interact with an object of an unknown type, the JVM will simply look at the
object and see that it belongs to a particular class (just like ordinary RTTI)
but then, before it can do anything else, the Class object must be
loaded. Thus, the .class file for that particular type must still be
available to the JVM, either on the local machine or across the network. So the
true difference between RTTI and
reflection is that with RTTI, the compiler opens and examines the .class
file at compile-time. Put another way, you can call all the methods of an object
in the “normal” way. With reflection, the .class file is
unavailable at compile-time; it is opened and examined by the run-time
environment.
|
|
Il est important de noter qu'il n'y a rien de magique dans la réflexion.
Quand vous utilisez la réflexion pour interagir avec des objets de type inconnu, la JVM va
simplement regarder l'objet et voir qu'il appartient à une classe particulière (comme une RTTI
ordinaire) mais, avant toute autre chose, l'objet Class doit être chargé. Le
fichier .class pour ce type particulier doit donc être disponible pour la JVM,
soit localement sur la machine ou via le réseau. La vraie différence entre le RTTI et la
réflexion est donc qu'avec le RTTI, le compilateur ouvre et examine le fichier .class à la
compilation. Dit autrement, vous pouvez appeler toutes les méthodes d'un objet
“normalement”. Avec la réflexion, le fichier .class n'est pas
disponible à la compilation ; il est ouvert et examiné à l'exécution.
|
|
|
|
A class method extractor
|
|
Un extracteur de méthodes de classe
|
|
|
|
You’ll rarely need to use the
reflection tools directly; they’re in the language to support other Java
features, such as object serialization (Chapter 11), JavaBeans (Chapter 13), and
RMI (Chapter 15). However, there are times when it’s quite useful to be
able to dynamically extract information about a class. One extremely useful tool
is a class method extractor. As mentioned before, looking at a class definition
source code or online documentation shows only the methods that are defined or
overridden within that class definition. But there could be dozens more
available to you that have come from base classes. To locate these is both
tedious and time
consuming[60].
Fortunately, reflection provides a way to write a simple tool that will
automatically show you the entire interface. Here’s the way it
works:
|
|
Vous aurez rarement besoin d'utiliser directement les outils de réflexion ;
ils sont utilisés pour supporter d'autres caractéristiques de Java, telles que la sérialisation
(Chapitre 11), JavaBeans (Chapitre 13) et RMI (Chapitre 15). Néanmoins, il est quelquefois utile
d'extraire dynamiquement des informations sur une classe. Un outil très utile est un extracteur de
méthode de classe. Comme mentionné précédemment, chercher le code définissant une classe ou sa
documentation en ligne montre uniquement les méthodes définies ou redéfinies dans cette définition
de classe. Mais il peut y en avoir des douzaines d'autre qui proviennent des classes de base. Les
retrouver est fastidieux et long [60]. Heureusement, la
réflexion fournit un moyen d'écrire un outil simple qui va automatiquement montrer l'interface
entière. Voici comment il fonctionne :
|
|
|
|
//: c12:ShowMethods.java
// Using reflection to show all the methods of
// a class, even if the methods are defined in
// the base class.
import java.lang.reflect.*;
public class ShowMethods {
static final String usage =
"usage: \n" +
"ShowMethods qualified.class.name\n" +
"To show all methods in class or: \n" +
"ShowMethods qualified.class.name word\n" +
"To search for methods involving 'word'";
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
Constructor[] ctor = c.getConstructors();
if(args.length == 1) {
for (int i = 0; i < m.length; i++)
System.out.println(m[i]);
for (int i = 0; i < ctor.length; i++)
System.out.println(ctor[i]);
} else {
for (int i = 0; i < m.length; i++)
if(m[i].toString()
.indexOf(args[1])!= -1)
System.out.println(m[i]);
for (int i = 0; i < ctor.length; i++)
if(ctor[i].toString()
.indexOf(args[1])!= -1)
System.out.println(ctor[i]);
}
} catch(ClassNotFoundException e) {
System.err.println("No such class: " + e);
}
}
} ///:~
|
|
//: c12:ShowMethods.java // Utiliser la réflexion pour montrer toutes les méthodes // d'une classe, même si celles ci sont définies dans la // classe de base. import java.lang.reflect.*;
public class ShowMethods { static final String usage = "usage: \n" + "ShowMethods qualified.class.name\n" + "Pour montrer toutes les méthodes or: \n" + "ShowMethods qualified.class.name word\n" + "Pour rechercher les méthodes contenant 'word'"; public static void main(String[] args) { if(args.length < 1) { System.out.println(usage); System.exit(0); } try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); if(args.length == 1) { for (int i = 0; i < m.length; i++) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) System.out.println(ctor[i]); } else { for (int i = 0; i < m.length; i++) if(m[i].toString() .indexOf(args[1])!= -1) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) if(ctor[i].toString() .indexOf(args[1])!= -1) System.out.println(ctor[i]); } } catch(ClassNotFoundException e) { System.err.println("Classe non trouvée : " + e); } } } ///:~
|
|
|
|
The Class methods
getMethods( ) and
getConstructors( )
return an array of Method and Constructor, respectively. Each of
these classes has further methods to dissect the names, arguments, and return
values of the methods they represent. But you can also just use
toString( ), as is done here, to produce a String with the
entire method signature. The rest of the code is just for extracting command
line information, determining if a particular signature matches with your target
string (using
indexOf( )), and
printing the results.
|
|
Les méthodes de Class getMethods()
et getConstructors() retournent respectivement un tableau de
Method et Constructor. Chacune de ces classes a de plus des
méthodes pour obtenir les noms, arguments et valeur retournée des méthodes qu'elles représentent.
Mais vous pouvez aussi utiliser simplement toString(), comme ici, pour produire
une chaîne de caractères avec la signature complète de la méthode. Le reste du code sert juste pour
l'extraction des informations de la ligne de commande, déterminer si une signature particulière
correspond à votre chaîne cible (en utilisant indexOf()), et afficher le
résultat.
|
|
|
|
This shows reflection in action, since
the result produced by Class.forName( ) cannot be known at
compile-time, and therefore all the method signature information is being
extracted at run-time. If you investigate your online documentation on
reflection, you’ll see that there is enough support to actually set up and
make a method call on an object that’s totally unknown at compile-time
(there will be examples of this later in this book). Again, this is something
you may never need to do yourself—the support is there for RMI and so a
programming environment can manipulate JavaBeans—but it’s
interesting.
|
|
Ceci montre la réflexion en action, puisque le résultat de
Class.forName() ne peut pas être connu à la compilation, donc toutes les
informations sur la signature des méthodes est extraite à l'exécution. Si vous étudiez la
documentation en ligne sur la réflexion, vous verrez qu'il est possible de créér et d'appeler une
méthode d'un objet qui était totalement inconnu lors de la compilation (nous verrons des exemples
plus loin dans ce livre). Encore une fois, c'est quelque chose dont vous n'aurez peut être jamais
besoin de faire vous même- le support est là pour le RMI et la programmation par JavaBeans- mais il
est intéressant.
|
|
|
|
An interesting experiment is to run
|
|
Une expérience intéressante est de lancer :
|
|
|
|
java ShowMethods ShowMethods
|
|
java ShowMethods ShowMethods
|
|
|
|
This produces a listing that includes a
public default constructor, even though you can see from the code that no
constructor was defined. The constructor you see is the one that’s
automatically synthesized by the compiler. If you then make ShowMethods a
non-public class (that is, friendly), the synthesized default constructor
no longer shows up in the output. The
synthesized default constructor
is automatically given the same access as the class.
|
|
Ceci produit une liste qui inclut un constructeur par défaut
public, bien que vous puissiez voir à partir du code source qu'aucun constructeur
n'ait été défini. Le constructeur que vous voyez est celui qui est automatiquement généré par le
compilateur. Si vous définissez maintenant ShowMethods comme une classe non
public (par exemple, amie), le constructeur par défaut n'apparaît plus dans la
liste. Le constructeur pas défaut généré a automatiquement le même accès que la
classe.
|
|
|
|
The output for ShowMethods is
still a little tedious. For example, here’s a portion of the output
produced by invoking java ShowMethods java.lang.String:
|
|
L'affichage de ShowMethods est toujours un peu ennuyeuse.
Par exemple, voici une portion de l'affichage produit en invoquant java ShowMethods
java.lang.String :
|
|
|
|
public boolean
java.lang.String.startsWith(java.lang.String,int)
public boolean
java.lang.String.startsWith(java.lang.String)
public boolean
java.lang.String.endsWith(java.lang.String)
|
|
public boolean java.lang.String.startsWith(java.lang.String,int) public boolean java.lang.String.startsWith(java.lang.String) public boolean java.lang.String.endsWith(java.lang.String)
|
|
|
|
It would be even nicer if the qualifiers
like java.lang could be stripped off. The
StreamTokenizer class
introduced in the previous chapter can help create a tool to solve this
problem:
|
|
Il serait préférable que les préfixes comme java.lang
puissent être éliminés. La classe StreamTokenizer
introduite dans le chapitre précédent peut nous aider à créer un outil résolvant ce problème
:
|
|
|
|
//: com:bruceeckel:util:StripQualifiers.java
package com.bruceeckel.util;
import java.io.*;
public class StripQualifiers {
private StreamTokenizer st;
public StripQualifiers(String qualified) {
st = new StreamTokenizer(
new StringReader(qualified));
st.ordinaryChar(' '); // Keep the spaces
}
public String getNext() {
String s = null;
try {
int token = st.nextToken();
if(token != StreamTokenizer.TT_EOF) {
switch(st.ttype) {
case StreamTokenizer.TT_EOL:
s = null;
break;
case StreamTokenizer.TT_NUMBER:
s = Double.toString(st.nval);
break;
case StreamTokenizer.TT_WORD:
s = new String(st.sval);
break;
default: // single character in ttype
s = String.valueOf((char)st.ttype);
}
}
} catch(IOException e) {
System.err.println("Error fetching token");
}
return s;
}
public static String strip(String qualified) {
StripQualifiers sq =
new StripQualifiers(qualified);
String s = "", si;
while((si = sq.getNext()) != null) {
int lastDot = si.lastIndexOf('.');
if(lastDot != -1)
si = si.substring(lastDot + 1);
s += si;
}
return s;
}
} ///:~
|
|
//: com:bruceeckel:util:StripQualifiers.java package com.bruceeckel.util; import java.io.*;
public class StripQualifiers { private StreamTokenizer st; public StripQualifiers(String qualified) { st = new StreamTokenizer( new StringReader(qualified)); st.ordinaryChar(' '); // garde les espaces } public String getNext() { String s = null; try { int token = st.nextToken(); if(token != StreamTokenizer.TT_EOF) { switch(st.ttype) { case StreamTokenizer.TT_EOL: s = null; break; case StreamTokenizer.TT_NUMBER: s = Double.toString(st.nval); break; case StreamTokenizer.TT_WORD: s = new String(st.sval); break; default: //il y a un seul caractère dans ttype s = String.valueOf((char)st.ttype); } } } catch(IOException e) { System.err.println("Erreur recherche token"); } return s; } public static String strip(String qualified) { StripQualifiers sq = new StripQualifiers(qualified); String s = "", si; while((si = sq.getNext()) != null) { int lastDot = si.lastIndexOf('.'); if(lastDot != -1) si = si.substring(lastDot + 1); s += si; } return s; } } ///:~
|
|
|
|
To facilitate reuse, this class is placed
in com.bruceeckel.util. As you can see, this uses the
StreamTokenizer and String manipulation to do its
work.
|
|
Pour faciliter sa réutilisation, cette classe est placée dans
com.bruceeckel.util. Comme vous pouvez le voir, elle utilise la classe
StreamTokenizer et la manipulation des String pour effectuer son
travail.
|
|
|
|
The new version of the program uses the
above class to clean up the output:
|
|
La nouvelle version du programme utilise la classe ci-dessus pour clarifier
le résultat :
|
|
|
|
//: c12:ShowMethodsClean.java
// ShowMethods with the qualifiers stripped
// to make the results easier to read.
import java.lang.reflect.*;
import com.bruceeckel.util.*;
public class ShowMethodsClean {
static final String usage =
"usage: \n" +
"ShowMethodsClean qualified.class.name\n" +
"To show all methods in class or: \n" +
"ShowMethodsClean qualif.class.name word\n" +
"To search for methods involving 'word'";
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
Constructor[] ctor = c.getConstructors();
// Convert to an array of cleaned Strings:
String[] n =
new String[m.length + ctor.length];
for(int i = 0; i < m.length; i++) {
String s = m[i].toString();
n[i] = StripQualifiers.strip(s);
}
for(int i = 0; i < ctor.length; i++) {
String s = ctor[i].toString();
n[i + m.length] =
StripQualifiers.strip(s);
}
if(args.length == 1)
for (int i = 0; i < n.length; i++)
System.out.println(n[i]);
else
for (int i = 0; i < n.length; i++)
if(n[i].indexOf(args[1])!= -1)
System.out.println(n[i]);
} catch(ClassNotFoundException e) {
System.err.println("No such class: " + e);
}
}
} ///:~
|
|
//: c12:ShowMethodsClean.java // ShowMethods avec élimination des préfixes // pour faciliter la lecture du résultat. import java.lang.reflect.*; import com.bruceeckel.util.*;
public class ShowMethodsClean { static final String usage = "usage: \n" + "ShowMethodsClean qualified.class.name\n" + "Pour montrer toutes les méthodes or: \n" + "ShowMethodsClean qualified.class.name word\n" + "Pour rechercher les méthodes contenant 'word'";
public static void main(String[] args) { if(args.length < 1) { System.out.println(usage); System.exit(0); } try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); // Conversion en un tableau de chaînes simplifiées : String[] n = new String[m.length + ctor.length]; for(int i = 0; i < m.length; i++) { String s = m[i].toString(); n[i] = StripQualifiers.strip(s); } for(int i = 0; i < ctor.length; i++) { String s = ctor[i].toString(); n[i + m.length] = StripQualifiers.strip(s); } if(args.length == 1) for (int i = 0; i < n.length; i++) System.out.println(n[i]); else for (int i = 0; i < n.length; i++) if(n[i].indexOf(args[1])!= -1) System.out.println(n[i]); } catch(ClassNotFoundException e) { System.err.println("Classe non trouvée : " + e); } } } ///:~
|
|
|
|
The class ShowMethodsClean is
quite similar to the previous ShowMethods, except that it takes the
arrays of Method and Constructor and converts them into a single
array of String. Each of these String objects is then passed
through StripQualifiers.Strip( ) to remove all the method
qualification.
|
|
La classe ShowMethodsClean est semblable à la classe
ShowMethods, excepté qu'elle transforme les tableaux de Method et
Constructor en un seul tableau de String. Chaque
String est ensuite appliquée à StripQualifiers.strip() pour
enlever les préfixes des méthodes.
|
|
|
|
This tool can be a real time-saver while
you’re programming, when you can’t remember if a class has a
particular method and you don’t want to go walking through the class
hierarchy in the online documentation, or if you don’t know whether that
class can do anything with, for example, Color objects.
|
|
Cet outil peut réellement vous faire gagner du temps lorsque vous
programmez, quand vous ne vous souvenez pas si une classe a une méthode particulière et que vous ne
voulez pas explorer toute sa hiérarchie dans la documentation en ligne, ou si vous ne savez pas si
cette classe peut faire quelque chose avec, par exemple, un objet
Color.
|
|
|
|
Chapter 13 contains a GUI version of this
program (customized to extract information for Swing components) so you can
leave it running while you’re writing code, to allow quick
lookups.
|
|
Le chapitre 13 contient une version graphique de ce programme (adapté pour
extraire des informations sur les composants Swing) que vous pouvez laisser tourner pendant que
vous écrivez votre code, pour des recherches rapides.
|
|
|
|
Summary
|
|
Résumé
|
|
|
|
RTTI allows you to discover type
information from an anonymous base-class reference. Thus, it’s ripe for
misuse by the novice since it might make sense before
polymorphic method calls do. For many people coming from a procedural
background, it’s difficult not to organize their programs into sets of
switch statements. They could accomplish this with RTTI and thus lose the
important value of polymorphism in code development and
maintenance. The intent of Java is that you use polymorphic method calls
throughout your code, and you use RTTI only when you must.
|
|
L'identification dynamique de type (RTTI) permet de découvrir des
informations de type à partir d'une référence sur une classe de base inconnue. [je n'arrive pas à
traduire cette phrase: Thus, it’s ripe for misuse by the novice since it might make
sense before polymorphic method calls do. Prop1:Ainsi, il mûrit pour sa mauvaise utilisation
par le novice puisque il pourrait être utilise de le faire avant un appel de méthode polymorphique.
Prop2 (JQ): Malheureusement ces informations peuvent conduire le novice à négliger les concepts du
polymorphisme, puisqu'elles sont plus faciles à appréhender.] Pour beaucoup de gens habitués à la
programmation procédurale, il est difficile de ne pas organiser leurs programmes en ensembles
d'expressions switch. Ils pourraient faire la même chose avec le RTTI et
perdraient ainsi l'importante valeur du polymorphisme dans le développement et la maintenance
du code. L'intention de Java est de vous faire utiliser des appels de méthodes polymorphiques dans
votre code, et de vous faire utiliser le RTTI uniquement lorsque c'est nécessaire.
|
|
|
|
However, using polymorphic method calls
as they are intended requires that you have control of the base-class definition
because at some point in the extension of your program you might discover that
the base class doesn’t include the method you need. If the base class
comes from a library or is otherwise controlled by someone else, a solution to
the problem is RTTI: You can inherit a new type and add your extra method.
Elsewhere in the code you can detect your particular type and call that special
method. This doesn’t destroy the polymorphism and extensibility of the
program because adding a new type will not require you to hunt for switch
statements in your program. However, when you add new code in your main body
that requires your new feature, you must use RTTI to detect your particular
type.
|
|
Néanmoins, utiliser des appels de méthodes polymorphiques nécessite que
vous ayez le contrôle de la définition des classes de base car il est possible que lors du
développement de votre programme vous découvriez que la classe de base ne contient pas une méthode
dans vous avez besoin. Si la classe de base provient d'une bibliothèque ou si elle est contrôlée
par quelqu'un d'autre, une solution à ce problème est le RTTI : vous pouvez créer un nouveau
type héritant de cette classe auquel vous ajoutez la méthode manquante. Ailleurs dans le
code, vous détectez ce type particulier et appelez cette méthode spécifique. Ceci ne détruit ni le
polymorphisme ni l'extensibilité du programme car ajouter un nouveau type ne vous oblige pas à
chasser les expressions switch dans votre programme. Cependant, lorsque vous ajoutez du code qui
requiert cette nouvelle fonctionnalité dans votre programme principal, vous devez utiliser le RTTI
pour détecter ce type particulier.
|
|
|
|
Putting a feature in a base class might
mean that, for the benefit of one particular class, all of the other classes
derived from that base require some meaningless stub of a method. This makes the
interface less clear and annoys those who must override abstract methods when
they derive from that base class. For example, consider a class hierarchy
representing musical instruments. Suppose you wanted to clear the spit valves of
all the appropriate instruments in your orchestra. One option is to put a
clearSpitValve( ) method in the base class Instrument, but
this is confusing because it implies that Percussion and
Electronic instruments also have spit valves. RTTI provides a much more
reasonable solution in this case because you can place the method in the
specific class (Wind in this case), where it’s appropriate.
However, a more appropriate solution is to put a
prepareInstrument( ) method in the base class, but you might not see
this when you’re first solving the problem and could mistakenly assume
that you must use RTTI.
|
|
Mettre la nouvelle caractéristique dans la classe de base peut signifier
que, pour le bénéfice d'une classe particulière, toutes les autres classes dérivées de cette classe
de base devront contenir des bouts de code inutiles de la méthode. Cela rend l'interface moins
claire et ennuie celui qui doit redéfinir des méthodes abstraites dérivant de la classe de base.
Supposez que vous désiriez nettoyer les becs? [spit valves] de tous les instruments à vent
de votre orchestre. Une solution est de mettre une méthode nettoyerBec() dans la
classe de base Instrument, mais c'est ennuyeux car cela implique que les
instruments Electroniques et à Percussion ont aussi un bec. Le
RTTI fournit une solution plus élégante dans ce cas car vous pouvez placer la méthode dans une
classe spécifique (Vent dans notre cas), où elle est appropriée. Néanmoins, une
solution encore meilleure est de mettre une méthode prepareInstrument() dans la
classe de base, mais il se peut que vous ne la trouviez pas la première fois que vous ayez à
résoudre le problème, et croyiez à tort que l'utilisation du RTTI est nécessaire.
|
|
|
|
Finally, RTTI will sometimes solve
efficiency problems. If your code nicely uses polymorphism, but it turns out
that one of your objects reacts to this general purpose code in a horribly
inefficient way, you can pick out that type using RTTI and write case-specific
code to improve the efficiency. Be wary, however, of programming for efficiency
too soon. It’s a seductive trap. It’s best to get the program
working first, then decide if it’s running fast enough, and only
then should you attack efficiency issues—with a
profiler.
|
|
Enfin, le RTTI permettra parfois de résoudre des problèmes d'efficacité. Si
votre code utilise le polymorphisme, mais qu'il s'avère que l'un de vos objets réagisse à ce code
très général d'une manière particulièrement inefficace, vous pouvez reconnaître ce type en
utilisant le RTTI et écrire un morceau de code spécifique pour améliorer son efficacité. Attention
toutefois à programmer pour l'efficacité trop tôt. C'est un piège séduisant. Il est
préférable d'avoir un programme qui marche d'abord, et décider ensuite s'il est assez rapide, et
seulement à ce moment là vous attaquer aux problèmes de performances — avec un
profiler.
|
|
|
|
Exercises
|
|
Exercices
|
|
|
|
Solutions to selected exercises
can be found in the electronic document The Thinking in Java Annotated
Solution Guide, available for a small fee from
www.BruceEckel.com.
|
|
Les solutions de certains exercices se trouvent dans le document
électronique Guide des solutions annoté de Penser en Java, disponible à prix réduit depuis
www.BruceEckel.com.
|
|
|
|
- Add Rhomboid to
Shapes.java. Create a Rhomboid, upcast it to a Shape, then
downcast it back to a Rhomboid. Try downcasting to a Circle and
see what
happens.
- Modify
Exercise 1 so that it uses instanceof to check the type before performing
the downcast.
- Modify
Shapes.java so that it can “highlight” (set a flag) in all
shapes of a particular type. The toString( ) method for each derived
Shape should indicate whether that Shape is
“highlighted.”
- Modify
SweetShop.java so that each type of object creation is controlled by a
command-line argument. That is, if your command line is “java SweetShop
Candy,” then only the Candy object is created. Notice how you
can control which Class objects are loaded via the command-line
argument.
- Add a new
type of Pet to PetCount3.java. Verify that it is created and
counted correctly in
main( ).
- Write
a method that takes an object and recursively prints all the classes in that
object’s
hierarchy.
- Modify
Exercise 6 so that it uses
Class.getDeclaredFields( )
to also display information about the fields in a
class.
- In
ToyTest.java, comment out Toy’s default constructor and
explain what
happens.
- Incorporate
a new kind of interface into ToyTest.java and verify that it is
detected and displayed
properly.
- Create a
new type of container that uses a private ArrayList to hold the
objects. Capture the type of the first object you put in it, and then
allow the user to insert objects of only that type from then
on.
- Write a program
to determine whether an array of char is a primitive type or a true
object.
- Implement
clearSpitValve( ) as described in the
summary.
- Implement
the rotate(Shape) method described in this chapter, such that it checks
to see if it is rotating a Circle (and, if so, doesn’t perform the
operation).
- Modify
Exercise 6 so that it uses reflection instead of
RTTI.
- Modify
Exercise 7 so that it uses reflection instead of
RTTI.
- In
ToyTest.java, use reflection to create a Toy object using the
nondefault
constructor.
- Look up
the interface for java.lang.Class in the HTML Java documentation from
java.sun.com. Write a program that takes the name of a class as a
command-line argument, then uses the Class methods to dump all the
information available for that class. Test your program with a standard library
class and a class you create.
|
|
- Ajouter Rhomboïde àFormes.java. Créer un
Rhomboïde, le transtyper en une Forme, ensuite le re-transtyper
en un Rhomboïde. Essayer de le transtyper en un Cercle et voir ce
qui se passe.
- Modifier l'exercice 1 afin d'utiliser instanceof pour
vérifier le type avant d'effectuer le transtypage descendant.
- Modifier Formes.java afin que toutes les formes d'un type
particulier puissent être mises en surbrillance (utiliser un drapeau). La méthode
toString() pour chaque classe dérivée de Forme devra indiquer si
cette Forme est mise en surbrillance ou pas.
- Modifier Confiseur.java afin que la création de chaque
type d'objet soit contrôlée par la ligne de commande. Par exemple, si la ligne de commande est
“java Confiseur Bonbon”, seul l'objet Bonbon sera
créé. Noter comment vous pouvez contrôler quels objets Class sont chargés via la
ligne de commande.
- Ajouter un nouveau type de Pet à
PetCount3.java. Vérifier qu'il est correctement créé et compté dans la méthode
main().
- Écrire une méthode qui prend un objet en paramètre et affiche récursivement
tous les classes de sa hiérarchie.
- Modifier l'exercice 6 afin d'utiliser
Class.getDeclaredFields() pour afficher aussi les informations sur les champs de
chaque classe.
- Dans ToyTest.java, commenter le constructeur par défaut de
Toy et expliquer ce qui arrive.
- Ajouter une nouvelle interface dans
ToyTest.java et vérifier qu'elle est correctement détectée et affichée.
- Créer un nouveau type de conteneur qui utilise une private
ArrayList pour stocker les objets. Déterminer le type du premier objet déposé, ne
permettre ensuite à l'utilisateur que d'insérer des objets de ce type.
- Écrire un programme qui détermine si un tableau de char
est un type primitif ou réellement un objet.
- Implanter nettoyerBec( ) comme décrit dans le
résumé.
- Modifier l'exercice 6 afin d'utiliser la réflexion à la place du
RTTI.
- Modifier l'exercice 7 afin d'utiliser la réflexion à la place du
RTTI.
- Dans ToyTest.java, utiliser la réflexion pour créer un
objet Toy en n'utilisant pas le constructeur par défaut.
- Étudier l'interface java.lang.Class dans la documentation
HTML de Java à java.sun.com. Écrire un programme qui prend en paramètre le nom d'une
classe via la ligne de commande, et utilise les méthodes de Class pour extraire
toutes les informations disponibles pour cette classe. Tester le programme sur une classe de la
bibliothèque standard et sur une des vôtres.
|
|
|
|
[60]
Especially in the past. However, Sun has greatly improved its HTML Java
documentation so that it’s easier to see base-class
methods.
[ Previous Chapter ]
[ Short TOC ]
[ Table of Contents ]
[ Index ]
[ Next Chapter ]
Last Update:04/24/2000
|
|
[60] Spécialement
dans le passé. Néanmoins, Sun a grandement amélioré la documentation HTML de Java et il est
maintenant plus aisé de voir les méthodes des classes de base.
|
|
|
|
|
|
|
|
|
|
|
|
|