 |
 |
6) Réutiliser les classes |
|
 |
|
Texte original |
 |
Traducteur : Olivier THOMANN |
|
 |
///
|
Ce chapitre contient 4 pages
1
2
3
4
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
What’s interesting in this example
is the tune( ) method, which accepts an Instrument reference.
However, in Wind.main( ) the tune( ) method is
called by giving it a Wind reference. Given that Java is particular about
type checking, it seems strange that a method that accepts one type will readily
accept another type, until you realize that a Wind object is also an
Instrument object, and there’s no method that tune( )
could call for an Instrument that isn’t also in Wind. Inside
tune( ), the code works for Instrument and anything derived
from Instrument, and the act of converting a Wind reference into
an Instrument reference is called
upcasting.
|
 |
Le point intéressant de cet exemple est la méthode
tune( ), qui accepte une référence Instrument. Cependant,
dans Wind.main( ) la méthode tune( ) est appelée en lui
donnant une référence Wind. Étant donné que Java est strict au sujet de la
vérification de type, il semble étrange qu'une méthode qui accepte un type acceptera littéralement
un autre type jusqu'à ce qu'on réalise qu'un objet Wind est également un objet
Instrument, et il n'y a pas de méthode que tune( ) pourrait
appeler pour un Instrument qui ne serait également dans Wind. À
l'intérieur de tune( ), le code fonctionne pour Instrument
et tout ce qui dérive de Instrument, et l'acte de convertir une référence
Wind en une référence Instrument est appelé transtypage
ascendant.
|
 |
 |
 |
Why “upcasting”?
|
 |
Pourquoi le transtypage ascendant ?
|
 |
 |
 |
The reason for the term is historical,
and based on the way class inheritance diagrams
have
traditionally been drawn: with the root at the top of the page, growing
downward. (Of course, you can draw your diagrams any way you find helpful.) The
inheritance diagram for Wind.java is then:
|
 |
La raison de ce terme est historique et basée sur la manière dont les
diagrammes d'héritage ont été traditionnellement dessinés : avec la racine au sommet de
la page, et grandissant vers le bas. Bien sûr vous pouvez dessiner vos diagrammes de la manière que
vous trouvez le plus pratique. Le diagramme d'héritage pour Wind.java
est :
|
 |
 |
 |
Casting from derived to base moves
up on the inheritance diagram, so it’s commonly referred to as
upcasting. Upcasting is always safe because you’re going from a
more specific type to a more general type. That is, the derived class is a
superset of the base class. It might contain more methods than the base class,
but it must contain at least the methods in the base class. The only
thing that can occur to the class interface during the upcast is that it can
lose methods, not gain them. This is why the compiler allows upcasting without
any explicit casts or other special notation.
|
 |
Transtyper depuis une classe dérivée vers la classe de base nous déplace
vers le haut dans le diagramme, on fait donc communément référence à un
transtypage ascendant. Le transtypage ascendant est toujours sans danger parce qu'on va
d'un type plus spécifique vers un type plus général. La classe dérivée est un sur-ensemble de la
classe de base. Elle peut contenir plus de méthodes que la classe de base, mais elle contient
au moins les méthodes de la classe de base. La seule chose qui puisse arriver à une classe
pendant le transtypage ascendant est de perdre des méthodes et non en gagner. C'est pourquoi le
compilateur permet le transtypage ascendant sans transtypage explicite ou une notation
spéciale.
|
 |
 |
 |
You can also perform the reverse of
upcasting, called downcasting, but this involves a
dilemma that is the subject of Chapter
12.
|
 |
On peut également faire l'inverse du transtypage ascendant, appelé
transtypage descendant, mais cela génère un dilemme qui est le sujet du chapitre
12.
|
 |
 |
 |
Composition vs. inheritance revisited
|
 |
Composition à la place de l'héritage revisité
|
 |
 |
 |
In object-oriented programming, the most
likely way that you’ll create and use code is by simply packaging data and
methods together into a class, and using objects of that class. You’ll
also use existing classes to build new classes with composition. Less
frequently, you’ll use inheritance. So although inheritance gets a lot of
emphasis while learning OOP, it doesn’t mean that you should use it
everywhere you possibly can. On the contrary, you should use it sparingly, only
when it’s clear that inheritance is useful.
One of the clearest ways to
determine whether you should use composition or inheritance is to ask whether
you’ll ever need to upcast from your new class to the base class. If you
must upcast, then inheritance is necessary, but if you don’t need to
upcast, then you should look closely at whether you need inheritance. The next
chapter (polymorphism) provides one of the most compelling reasons for
upcasting, but if you remember to ask “Do I need to upcast?”
you’ll have a good tool for deciding between composition and
inheritance.
|
 |
En programmation orienté objet, la manière la plus probable pour créer et
utiliser du code est simplement de mettre des méthodes et des données ensemble dans une classe puis
d'utiliser les objets de cette classe. On utilisera également les classes existantes pour
construire les nouvelles classes avec la composition. Moins fréquemment on utilisera l'héritage.
Donc bien qu'on insiste beaucoup sur l'héritage en apprenant la programmation orientée objet, cela
ne signifie pas qu'on doive l'utiliser partout où l'on peut. Au contraire, on devrait l'utiliser
avec parcimonie, seulement quand il est clair que l'héritage est utile. Un des moyens les plus
clairs pour déterminer si on doit utiliser la composition ou l'héritage est de se demander si on
aura jamais besoin de faire un transtypage ascendant de la nouvelle classe vers la classe de base.
Si on doit faire un transtypage ascendant, alors l'héritage est nécessaire, mais si on n'a pas
besoin de faire un transtypage ascendant, alors il faut regarder avec attention pour savoir si on a
besoin de l'héritage. Le prochain chapitre (polymorphisme) fournit une des plus excitantes raisons
pour le transtypage ascendant, mais si vous vous rappelez de vous demander « Ai-je besoin de
transtypage ascendant ? », vous aurez un bon outil pour décider entre composition et
héritage.
|
 |
 |
 |
The final keyword
|
 |
Le mot clé final
|
 |
 |
 |
Java’s
final keyword has slightly different meanings
depending on the context, but in general it says “This cannot be
changed.” You might want to prevent changes for two reasons: design or
efficiency. Because these two reasons are quite different, it’s possible
to misuse the final keyword.
|
 |
Le mot clé Java final a des sens légèrement différents
suivant le contexte, mais en général il signifie « Cela ne peut pas changer ». Vous
pourriez vouloir empêcher les changements pour deux raisons : conception ou efficacité.
Parce que ces deux raisons sont quelque peu différentes, il est possible de mal utiliser le mot clé
final.
|
 |
 |
 |
The following sections discuss the three
places where final can be used: for data, methods, and
classes.
|
 |
Les sections suivantes parlent des trois endroits où le mot clé
final peut être utilisé : données, méthodes et classes.
|
 |
 |
 |
Final data
|
 |
Données finales
|
 |
 |
 |
Many programming languages have a way to
tell the compiler that a piece of data is “constant.” A constant is
useful for two reasons:
|
 |
Beaucoup de langages de programmation ont un moyen de dire au compilateur
que cette donnée est constante. Une constante est utile pour deux raisons:
|
 |
 |
 |
- It can be a
compile-time constant that won’t ever
change.
- It can be a
value initialized at run-time that you don’t want
changed.
|
 |
- Elle peut être une constante lors de la compilation qui ne
changera jamais ;
- Elle peut être une valeur initialisée à l'exécution qu'on ne veut pas
changer.
|
 |
 |
 |
In the case of a
compile-time constant, the compiler is allowed to “fold” the
constant value into any
calculations in which it’s used; that is, the calculation can be performed
at compile-time, eliminating some run-time overhead. In Java, these sorts of
constants must be primitives and are expressed using the
final keyword. A value must be given at the time of definition of such a
constant.
|
 |
Dans le cas d'une constante à la compilation, le compilateur inclut
« en dur » la valeur de la constante pour tous les calculs où elle
intervient ; dans ce cas, le calcul peut être effectué à la compilation, éliminant
ainsi un surcoût à l'exécution. En Java, ces sortes de constantes doivent être des primitives et
sont exprimées en utilisant le mot-clé final. Une valeur doit être donnée au
moment de la définition d'une telle constante.
|
 |
 |
 |
A field that is both
static and final has
only one piece of storage that cannot be changed.
|
 |
Un champ qui est à la fois static et
final a un emplacement de stockage fixe qui ne peut pas être changé.
|
 |
 |
 |
When using
final
with object references rather than primitives the meaning gets a bit confusing.
With a primitive, final makes the value a constant, but with an
object reference, final makes the reference a constant. Once the
reference is initialized to an object, it can never be changed to point to
another object. However, the object itself can be modified; Java does not
provide a way to make any arbitrary object a constant. (You can, however, write
your class so that objects have the effect of being constant.) This restriction
includes arrays, which are also objects.
|
 |
Quand on utilise final avec des objets références plutôt
qu'avec des types primitifs la signification devient un peu confuse. Avec un type primitif,
final fait de la valeur une constante, mais avec un objet référence,
final fait de la « référence » une constante. Une fois la référence liée
à un objet, elle ne peut jamais changer pour pointer vers un autre objet. Quoiqu'il en soit,
l'objet lui même peut être modifié ; Java ne fournit pas de moyen de rendre un objet
arbitraire une constante. On peut quoiqu'il en soit écrire notre classe de manière que les objets
paraissent constants. Cette restriction inclut les tableaux, qui sont également des
objets.
|
 |
 |
 |
Here’s an example that demonstrates
final fields:
|
 |
Voici un exemple qui montre les champs final:
|
 |
 |
 |
//: c06:FinalData.java // The effect of final on fields.
class Value { int i = 1; }
public class FinalData { // Can be compile-time constants final int i1 = 9; static final int VAL_TWO = 99; // Typical public constant: public static final int VAL_THREE = 39; // Cannot be compile-time constants: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20); Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); // Arrays: final int[] a = { 1, 2, 3, 4, 5, 6 };
public void print(String id) { System.out.println( id + ": " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); //! fd1.i1++; // Error: can't change value fd1.v2.i++; // Object isn't constant! fd1.v1 = new Value(); // OK -- not final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // Object isn't constant! //! fd1.v2 = new Value(); // Error: Can't //! fd1.v3 = new Value(); // change reference //! fd1.a = new int[3];
fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } } ///:~
|
 |
// ! c06:FinalData.java // L'effet de final sur les champs.
class Value { int i = 1; }
public class FinalData { // Peut être des constantes à la compilation final int i1 = 9; static final int VAL_TWO = 99; // Constantes publiques typiques: public static final int VAL_THREE = 39; // Ne peuvent pas être des constantes à la compilation: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20); Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); // Tableaux: final int[] a = { 1, 2, 3, 4, 5, 6 };
public void print(String id) { System.out.println( id + " : " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); // ! fd1.i1++; // Erreur : on ne peut pas changer la valeur fd1.v2.i++; // L'objet n'est pas une constante! fd1.v1 = new Value(); // OK -- non final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // L'objet n'est pas une constante! // ! fd1.v2 = new Value(); // Erreur : Ne peut pas // ! fd1.v3 = new Value(); // changer la référence // ! fd1.a = new int[3];
fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } } ///:~
|
 |
 |
 |
Since i1 and VAL_TWO are
final primitives with compile-time values, they can both be used as
compile-time constants and are not different in any important way.
VAL_THREE is the more typical way you’ll see such constants
defined: public so they’re usable outside the package,
static to emphasize that there’s only one, and final to say
that it’s a constant. Note that
final
static primitives with constant initial values (that is, compile-time
constants) are named with all capitals by convention, with words separated by
underscores (This is just like C constants, which is where the convention
originated.) Also note that i5 cannot be known at compile-time, so it is
not capitalized.
|
 |
Etant donné que i1 et VAL_TWO sont des
primitives final ayant une valeur à la compilation, elles peuvent être toutes les
deux utilisées comme constantes à la compilation et ne sont pas vraiment différentes.
VAL_THREE nous montre la manière la plus typique de définir ces
constantes : public afin qu'elles puissent être utilisées en dehors du
package, static pour souligner qu'il ne peut y en avoir qu'une seulement, et
final pour dire que c'est une constante. Notez que les primitives final
static avec des valeurs initiales constantes (ce sont des constantes à la compilation)
sont nommées avec des lettre capitales par convention, avec des mots séparés par des underscores.
Ce sont comme des constantes C, d'où cette convention est originaire. Notons également que
i5 ne peut pas être connu à la compilation, donc elle n'est pas en lettres
capitales.
|
 |
 |
 |
Just because something is final
doesn’t mean that its value is known at compile-time. This is demonstrated
by initializing i4 and i5 at run-time using randomly generated
numbers. This portion of the example also shows the difference between making a
final value static or non-static. This difference shows up
only when the values are initialized at run-time, since the compile-time values
are treated the same by the compiler. (And presumably optimized out of
existence.) The difference is shown in the output from one run:
|
 |
Le fait que quelque chose soit final ne signifie pas que
sa valeur est connue à la compilation. Ceci est montré par l'initialisation de i4
et i5 à l'exécution en utilisant des nombres générés aléatoirement. La portion de
cet exemple montre également la différence entre mettre une valeur final
static ou non static. Cette différence n'est visible que quand
les valeurs sont initialisées à l'exécution, tandis que les valeurs à la compilation sont traitées
de même par le compilateur. Et vraisemblablement optimisées à la compilation. La différence est
montrée par la sortie d'une exécution:
|
 |
 |
 |
fd1: i4 = 15, i5 = 9 Creating new FinalData fd1: i4 = 15, i5 = 9 fd2: i4 = 10, i5 = 9
|
 |
fd1 : i4 = 15, i5 = 9 Creating new FinalData fd1 : i4 = 15, i5 = 9 fd2 : i4 = 10, i5 = 9
|
 |
 |
 |
Note that the values of i4 for
fd1 and fd2 are unique, but the value for i5 is not changed
by creating the second FinalData object. That’s because it’s
static and is initialized once upon loading and not each time a new
object is created.
|
 |
Notez que les valeurs de i4 pour fd1 et
fd2 sont uniques, mais la valeur de i5 n'est pas changée en
créant un second objet FinalData. C'est parce qu'elle est static
et initialisée une fois pour toutes lors du chargement et non à chaque fois qu'un nouvel objet est
créé.
|
 |
 |
 |
The variables v1 through v4
demonstrate the meaning of a final reference. As you can see in
main( ), just because v2 is final doesn’t mean
that you can’t change its value. However, you cannot rebind v2 to a
new object, precisely because it’s final. That’s what
final means for a reference. You can also see the same meaning holds true
for an array, which is just another kind of reference. (There is no way that I
know of to make the array references themselves final.) Making references
final seems less useful than making primitives
final.
|
 |
Les variables v1 jusqu'à v4 montre le
sens de références final. Comme on peut le voir dans main(), le
fait que v2 soit final ne signifie pas qu'on ne peut pas changer
sa valeur. Quoiqu'il en soit, on ne peut pas réaffecter un nouvel objet à v2,
précisément parce qu'il est final. C'est ce que final signifie
pour une référence. On peut également voir que ce sens reste vrai pour un tableau, qui est une
autre sorte de référence. Il n'y a aucun moyen de savoir comment rendre les références du tableau
elle-mêmes final. Mettre les références final semble moins utile
que mettre les primitives final.
|
 |
 |
 |
Blank finals
|
 |
Finals sans initialisation
|
 |
 |
 |
Java allows the creation of
blank finals, which are
fields that are declared as final but are not given an initialization
value. In all cases, the blank final must be initialized before it is
used, and the compiler ensures this. However, blank finals provide much more
flexibility in the use of the final keyword since, for example, a
final field inside a class can now be different for each object and yet
it retains its immutable quality. Here’s an example:
|
 |
Java permet la création de finals sans initialisation, qui sont
des champs déclarés final, mais n'ont pas de valeur d'initialisation. Dans tous
les cas, un final sans initialisation doit être initialisé avant d'être utilisé, et le
compilateur doit s'en assurer. Quoiqu'il en soit, les finals sans initialisation fournissent bien
plus de flexibilité dans l'usage du mot-clé final depuis que, par exemple, un
champ final à l'intérieur d'une classe peut maintenant être différent pour chaque
objet tout en gardant son caractère immuable. Voici un exemple:
|
 |
 |
 |
//: c06:BlankFinal.java // "Blank" final data members.
class Poppet { }
class BlankFinal { final int i = 0; // Initialized final final int j; // Blank final final Poppet p; // Blank final reference // Blank finals MUST be initialized // in the constructor: BlankFinal() { j = 1; // Initialize blank final p = new Poppet(); } BlankFinal(int x) { j = x; // Initialize blank final p = new Poppet(); } public static void main(String[] args) { BlankFinal bf = new BlankFinal(); } } ///:~
|
 |
// ! c06:BlankFinal.java // Les membres des données final sans initialisation
class Poppet { }
class BlankFinal { final int i = 0; // Final initialisé final int j; // Final sans initialisation final Poppet p; // Référence final sans initialisation // Les finals doivent être initialisés // dans le constructeur: BlankFinal() { j = 1; // Initialise le final sans valeur initiale p = new Poppet(); } BlankFinal(int x) { j = x; // Initialise le final sans valeur initiale p = new Poppet(); } public static void main(String[] args) { BlankFinal bf = new BlankFinal(); } } ///:~
|
 |
 |
 |
You’re forced to perform
assignments to finals either with an expression at the point of
definition of the field or in every constructor. This way it’s guaranteed
that the final field is always initialized before use.
|
 |
Vous êtes forcés d'initialiser un final soit avec une
expression au moment de la définition, soit dans chaque constructeur. De cette manière il est
garanti que le champ final sera toujours initialisé avant son
utilisation.
|
 |
 |
 |
Final arguments
|
 |
Arguments final
|
 |
 |
 |
Java allows you to make
arguments final by
declaring them as such in the argument list. This means that inside the method
you cannot change what the argument reference points to:
|
 |
Java permet de définir les arguments final en les
déclarant comme tels dans la liste des arguments. Cela signifie qu'à l'intérieur de la méthode on
ne peut pas changer ce vers quoi pointe l'argument:
|
 |
 |
 |
//: c06:FinalArguments.java // Using "final" with method arguments.
class Gizmo { public void spin() {} }
public class FinalArguments { void with(final Gizmo g) { //! g = new Gizmo(); // Illegal -- g is final } void without(Gizmo g) { g = new Gizmo(); // OK -- g not final g.spin(); } // void f(final int i) { i++; } // Can't change // You can only read from a final primitive: int g(final int i) { return i + 1; } public static void main(String[] args) { FinalArguments bf = new FinalArguments(); bf.without(null); bf.with(null); } } ///:~
|
 |
// ! c06:FinalArguments.java // Utilisation de « final » dans les arguments d'une méthode.
class Gizmo { public void spin() {} }
public class FinalArguments { void with(final Gizmo g) { // ! g = new Gizmo(); // Illégal -- g est final } void without(Gizmo g) { g = new Gizmo(); // OK -- g n'est pas final g.spin(); } // void f(final int i) { i++; } // Ne peut pas changer // On peut seulement lire depuis une primitive final: int g(final int i) { return i + 1; } public static void main(String[] args) { FinalArguments bf = new FinalArguments(); bf.without(null); bf.with(null); } } ///:~
|
 |
 |
 |
Note that you can still assign a
null reference to an argument that’s final without the compiler
catching it, just like you can with a non-final
argument.
|
 |
A noter qu'on peut encore affecter une référence null à un
argument qui est final sans que le compilateur ne l'empêche, comme on pourrait le faire pour un
argument non-final.
|
 |
 |
 |
The methods f( ) and
g( ) show what happens when primitive arguments are final:
you can read the argument, but you can't change
it.
|
 |
Les méthodes f( ) et g( )
montre ce qui arrive quand les arguments primitifs sont final: on peut lire
l'argument, mais on ne peut pas le changer.
|
 |
 |
 |
Final methods
|
 |
Méthodes final
|
 |
 |
 |
There are two reasons for
final methods. The first is
to put a “lock” on the method to prevent any inheriting class from
changing its meaning. This is done for design reasons when you want to make sure
that a method’s behavior is retained during inheritance and cannot be
overridden.
|
 |
Les méthodes final ont deux raisons d'être. La première
est de mettre un « verrou » sur la méthode pour empêcher toute sous-classe de la
redéfinir. Ceci est fait pour des raisons de conception quand on veut être sûr que le comportement
d'une méthode est préservé durant l'héritage et ne peut pas être redéfini.
|
 |
 |
 |
The second reason for final
methods is efficiency. If you make a method final, you are allowing the
compiler to turn any calls to that method into
inline calls. When the
compiler sees a final method call it can (at its discretion) skip the
normal approach of inserting code to perform the method call mechanism (push
arguments on the stack, hop over to the method code and execute it, hop back and
clean off the stack arguments, and deal with the return value) and instead
replace the method call with a copy of the actual code in the method body. This
eliminates the overhead of the method call. Of course, if a method is big, then
your code begins to bloat and you probably won’t see any performance gains
from inlining, since any improvements will be dwarfed by the amount of time
spent inside the method. It is implied that the Java compiler is able to detect
these situations and choose wisely whether to inline a final method.
However, it’s better to not trust that the compiler is able to do this and
make a method final only if it’s quite small or if you want to
explicitly prevent overriding.
|
 |
La deuxième raison est l'efficacité. Si on met une méthode
final, on permet au compilateur de convertir tout appel à cette méthode en un
appel incorporé. Quand le compilateur voit un appel à une méthode final,
il peut à sa discrétion éviter l'approche normale d'insérer du code pour exécuter l'appel de la
méthode (mettre les arguments sur la pile, sauter au code de la méthode et l' exécuter, revenir au
code courant et nettoyer les arguments de la pile, s'occuper de la valeur de retour) et à la place
remplacer l'appel de méthode avec une copie du code de cette méthode dans le corps de la méthode
courante. Ceci élimine le surcoût de l'appel de méthode. Bien entendu, si une méthode est
importante, votre code commencera alors à grossir et vous ne verrez plus le gain de performance dû
au code « incorporé », parce que toute amélioration sera cachée par le temps passé à
l'intérieur de la méthode. Ceci implique que le compilateur Java est capable de détecter ces
situations et de choisir sagement si oui ou non il faut « incorporer » une méthode
final. Quoiqu'il en soit, il est mieux de ne pas faire confiance à ce que peut
faire le compilateur et de mettre une méthode final seulement si elle est plutôt
petite ou si on veut explicitement empêcher la surcharge.
|
 |
 |
 |
final and private
|
 |
final et private
|
 |
 |
 |
Any private methods in a class are
implicitly final. Because you can’t access a private method,
you can’t override it (even though the compiler doesn’t give an
error message if you try to override it, you haven’t overridden the
method, you’ve just created a new method). You can add the final
specifier to a private method but it doesn’t give that method any
extra meaning.
|
 |
Toutes les méthodes private sont implicitement
final. Parce qu'on ne peut pas accéder à une méthode private, on
ne peut pas la surcharger (même si le compilateur ne donne pas de messages d'erreur si on essaye de
la redéfinir, on ne redéfinit pas la méthode, on a simplement créé une nouvelle méthode). On peut
ajouter le mot-clé final à une méthode private, mais ça n'apporte
rien de plus.
|
 |
 |
 |
This issue can cause confusion, because
if you try to override a private method (which is implicitly
final) it seems to work:
|
 |
Ce problème peut rendre les choses un peu confuses, parce que si on essaye
de surcharger une méthode private qui est implicitement final ça
semble fonctionner:
|
 |
 |
 |
//: c06:FinalOverridingIllusion.java // It only looks like you can override // a private or private final method.
class WithFinals { // Identical to "private" alone: private final void f() { System.out.println("WithFinals.f()"); } // Also automatically "final": private void g() { System.out.println("WithFinals.g()"); } }
class OverridingPrivate extends WithFinals { private final void f() { System.out.println("OverridingPrivate.f()"); } private void g() { System.out.println("OverridingPrivate.g()"); } }
class OverridingPrivate2 extends OverridingPrivate { public final void f() { System.out.println("OverridingPrivate2.f()"); } public void g() { System.out.println("OverridingPrivate2.g()"); } }
public class FinalOverridingIllusion { public static void main(String[] args) { OverridingPrivate2 op2 = new OverridingPrivate2(); op2.f(); op2.g(); // You can upcast: OverridingPrivate op = op2; // But you can't call the methods: //! op.f(); //! op.g(); // Same here: WithFinals wf = op2; //! wf.f(); //! wf.g(); } } ///:~
|
 |
// ! c06:FinalOverridingIllusion.java // C'est seulement une impression qu'on peut // surcharger une méthode private ou private final.
class WithFinals { // Identique à « private » tout seul: private final void f() { System.out.println("WithFinals.f()"); } // Également automatiquement « final »: private void g() { System.out.println("WithFinals.g()"); } }
class OverridingPrivate extends WithFinals { private final void f() { System.out.println("OverridingPrivate.f()"); } private void g() { System.out.println("OverridingPrivate.g()"); } }
class OverridingPrivate2 extends OverridingPrivate { public final void f() { System.out.println("OverridingPrivate2.f()"); } public void g() { System.out.println("OverridingPrivate2.g()"); } }
public class FinalOverridingIllusion { public static void main(String[] args) { OverridingPrivate2 op2 = new OverridingPrivate2(); op2.f(); op2.g(); // On peut faire un transtypage ascendant: OverridingPrivate op = op2; // Mais on ne peut pas appeler les méthodes: // ! op.f(); // ! op.g(); // Idem ici: WithFinals wf = op2; // ! wf.f(); // ! wf.g(); } } ///:~
|
 |
 |
 |
“Overriding” can only occur
if something is part of the base-class interface. That is, you must be able to
upcast an object to its base type and call the same method (the point of this
will become clear in the next chapter). If a method is private, it
isn’t part of the base-class interface. It is just some code that’s
hidden away inside the class, and it just happens to have that name, but if you
create a public, protected or “friendly” method in the
derived class, there’s no connection to the method that might happen to
have that name in the base class. Since a private method is unreachable
and effectively invisible, it doesn’t factor into anything except for the
code organization of the class for which it was
defined.
|
 |
« Surcharger » peut seulement arriver si quelque chose fait
partie de l'interface de la classe de base. On doit être capable de faire un transtypage ascendant
vers la classe de base et d'appeler la même méthode. Ce point deviendra clair dans le prochain
chapitre. Si une méthode est private, elle ne fait pas partie de l'interface de la
classe de base. C'est simplement du code qui est caché à l'intérieur de la classe, et il arrive
simplement qu'elle a ce nom, mais si on définit une méthode public,
protected ou « amies » dans la classe dérivée, il n'y a aucune connexion
avec la méthode de même nom dans la classe de base. Étant donné qu'une méthode
private est inatteignable et effectivement invisible, elle ne sert à rien d'autre
qu'à l'organisation du code dans la classe dans laquelle elle est définie.
|
 |
 |
 |
Final classes
|
 |
Classes final
|
 |
 |
 |
When you say that an entire class is
final (by preceding its definition with the final keyword), you
state that you don’t want to inherit from this class or allow anyone else
to do so. In other words, for some reason the design of your class is such that
there is never a need to make any changes, or for safety or security reasons you
don’t want subclassing. Alternatively, you might be dealing with an
efficiency issue, and you want to make sure that any activity involved with
objects of this class are as efficient as possible.
|
 |
Quand on dit qu'une classe entière est final (en faisant
précéder sa définition par le mot-clé final) on stipule qu'on ne veut pas hériter
de cette classe ou permettre à qui que ce soit de le faire. En d'autres mots, soit la conception de
cette classe est telle qu'on n'aura jamais besoin de la modifier, soit pour des raisons de sûreté
ou de sécurité on ne veut pas qu'elle soit sous-classée. Ou alors, on peut avoir affaire à un
problème d'efficacité, et on veut s'assurer que toute activité impliquant des objets de cette
classe sera aussi efficace que possible.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |