t
t
t
t
t t   A) Passage et Retour d'Objets
tttt
t
carrea) Préface carreb) Avant-propos carre1) Introduction sur les &laqo; objets » carre2) Tout est &laqo; objet » carre3) Contrôle du flux du programme carre4) Initialization & Cleanup carre5) Cacher l'implémentation carre6) Réutiliser les classes carre7) Polymorphisme carre8) Interfaces & classes internes carre9) Stockage des objets carre10) Error Handling with Exceptions carre11) Le système d’E/S de Java carre12) Identification dynamique de type carre13) Création de fenêtres & d'Applets carre14) Les &laqo; Threads » multiples carre15) Informatique distribuée A) Passage et retour d'objets carreB) L'Interface Java Natif (JNI) carreC) Conseils pour une programation stylée en Java carreD) Resources
Texte original t Traducteur : Jérome Quelin
t
t
    
Ce chapitre contient 6 pages
1 2 3 4 5 6
\\\
t t t
t t t
t
t t t
t
05.07.01 - version 5.5 :
- Ajout des tags de séparation de pages (Armel).
28.04.01 - version 5.4 :
- Nettoyage du code html (titres, paragraphes, tableaux, images, ancres) par Armel.
17.07.2000 - version 5.3 :
- Corrections apportées par Olivier Thomann.
26.06.2000 - version 5.2 :
- Insertion du journal de log.
19.06.2000 - version 5.1 :
- Première publication sur eGroups.
Traducteur :
- Jérome QUELIN
Texte original :
-Thinking in Java, 2nd edition, Revision 10
© 2000 by Bruce Eckel
t t t
A: Passing & Returning Objects t A : Passage & et Retour d'Objets
t t t
By now you should be reasonably comfortable with the idea that when you’re “passing” an object, you’re actually passing a reference.
t Vous devriez maintenant être conscient que lorsque vous « passez »  un objet, vous passez en fait une référence sur cet objet.
t t t
In many programming languages you can use that language’s “regular” way to pass objects around, and most of the time everything works fine. But it always seems that there comes a point at which you must do something irregular and suddenly things get a bit more complicated (or in the case of C++, quite complicated). Java is no exception, and it’s important that you understand exactly what’s happening as you pass objects around and manipulate them. This appendix will provide that insight.
t Presque tous les langages de programmation possèdent une façon « normale » de passer des objets, et la plupart du temps tout se passe bien. Mais il arrive toujours un moment où on doit faire quelque chose d'un peu hors-norme, et alors les choses se compliquent un peu (voire beaucoup dans le cas du C++). Java ne fait pas exception à la règle, et il est important de comprendre exactement les mécanismes du passage d'arguments et de la manipulation des objets passés. Cette annexe fournit des précisions quant à ces mécanismes.
t t t
Another way to pose the question of this appendix, if you’re coming from a programming language so equipped, is “Does Java have pointers?” Some have claimed that pointers are hard and dangerous and therefore bad, and since Java is all goodness and light and will lift your earthly programming burdens, it cannot possibly contain such things. However, it’s more accurate to say that Java has pointers; indeed, every object identifier in Java (except for primitives) is one of these pointers, but their use is restricted and guarded not only by the compiler but by the run-time system. Or to put it another way, Java has pointers, but no pointer arithmetic. These are what I’ve been calling “references,” and you can think of them as “safety pointers,” not unlike the safety scissors of elementary school—they aren’t sharp, so you cannot hurt yourself without great effort, but they can sometimes be slow and tedious.
t Ou si vous préférez, si vous provenez d'un langage de programmation qui en disposait, cette annexe répond à la question « Est-ce que Java utilise des pointeurs ? ». Nombreux sont ceux qui ont affirmé que les pointeurs sont difficiles à manipuler et dangereux, donc à proscrire, et qu'en tant que langage propre et pur destiné à alléger le fardeau quotidien de la programmation, Java ne pouvait décemment contenir de telles choses. Cependant, il serait plus exact de dire que Java dispose de pointeurs ; en fait, chaque identifiant d'objet en Java (les scalaires exceptés) est un pointeur, mais leur utilisation est restreinte et surveillée non seulement par le compilateur mais aussi par le système d'exécution. Autrement dit, Java utilise les pointeurs, mais pas les pointeurs arithmétiques. C'est ce que j'ai appelé les « références » ; et vous pouvez y penser comme à des « pointeurs sécurisés », un peu comme des ciseaux de cours élémentaire - ils ne sont pas pointus, on ne peut donc se faire mal avec qu'en le cherchant bien, mais ils peuvent être lents et ennuyeux.
t t t

Passing references around

t

Passage de références

t t t
When you pass a reference into a method, you’re still pointing to the same object. A simple experiment demonstrates this:
t Quand on passe une référence à une méthode, on pointe toujours sur le même objet. Un simple test le démontre :
t t t
//: appendixa:PassReferences.java // Passing references around. public class PassReferences { static void f(PassReferences h) { System.out.println("h inside f(): " + h); } public static void main(String[] args) { PassReferences p = new PassReferences(); System.out.println("p inside main(): " + p); f(p); } } ///:~ t
//: appendixa:PassReferences.java
// Le passage de références.

public class PassReferences {
  static void f(PassReferences h) {
    System.out.println("h inside f(): " + h);
  }
  public static void main(String[] args) {
    PassReferences p = new PassReferences();
    System.out.println("p inside main(): " + p);
    f(p);
  }
} ///:~
t t t
The method toString( ) is automatically invoked in the print statements, and PassReferences inherits directly from Object with no redefinition of toString( ). Thus, Object’s version of toString( ) is used, which prints out the class of the object followed by the address where that object is located (not the reference, but the actual object storage). The output looks like this:
t La méthode toString() est automatiquement appelée dans l'instruction print, dont PassReferences hérite directement de Object comme la méthode toString() n'est pas redéfinie. La version toString() de Object est donc utilisée, qui affiche la classe de l'objet suivie de l'adresse mémoire où se trouve l'objet (non pas la référence, mais bien là où est stocké l'objet). La sortie ressemble à ceci :
t t t
p inside main(): PassReferences@1653748 h inside f(): PassReferences@1653748 t
p inside main(): PassReferences@1653748
h inside f(): PassReferences@1653748
t t t
You can see that both p and h refer to the same object. This is far more efficient than duplicating a new PassReferences object just so that you can send an argument to a method. But it brings up an important issue.
t On peut constater que p et h référencent bien le même objet. Ceci est bien plus efficace que de créer un nouvel objet PassReferences juste pour envoyer un argument à une méthode. Mais ceci amène une importante question.
t t t

Aliasing

t

Aliasing

t t t
Aliasing means that more than one reference is tied to the same object, as in the above example. The problem with aliasing occurs when someone writes to that object. If the owners of the other references aren’t expecting that object to change, they’ll be surprised. This can be demonstrated with a simple example:
t L'aliasing veut dire que plusieurs références peuvent être attachées au même objet, comme dans l'exemple précédent. Le problème de l'aliasing survient quand quelqu'un modifie cet objet. Si les propriétaires des autres références ne s'attendent pas à ce que l'objet change, ils vont avoir des surprises. Ceci peut être mis en évidence avec un simple exemple :
t t t
//: appendixa:Alias1.java // Aliasing two references to one object. public class Alias1 { int i; Alias1(int ii) { i = ii; } public static void main(String[] args) { Alias1 x = new Alias1(7); Alias1 y = x; // Assign the reference System.out.println("x: " + x.i); System.out.println("y: " + y.i); System.out.println("Incrementing x"); x.i++; System.out.println("x: " + x.i); System.out.println("y: " + y.i); } } ///:~ t
//: appendixa:Alias1.java
// Aliasing : deux références sur un même objet.

public class Alias1 {
  int i;
  Alias1(int ii) { i = ii; }
  public static void main(String[] args) {
    Alias1 x = new Alias1(7);
    Alias1 y = x; // Assigne la référence.
    System.out.println("x: " + x.i);
    System.out.println("y: " + y.i);
    System.out.println("Incrementing x");
    x.i++;
    System.out.println("x: " + x.i);
    System.out.println("y: " + y.i);
  }
} ///:~
t t t
In the line:
t Dans la ligne :
t t t
Alias1 y = x; // Assign the reference t
Alias1 y = x; // Assigne la référence.
t t t
a new Alias1 reference is created, but instead of being assigned to a fresh object created with new, it’s assigned to an existing reference. So the contents of reference x, which is the address of the object x is pointing to, is assigned to y, and thus both x and y are attached to the same object. So when x’s i is incremented in the statement:
t une nouvelle référence Alias1 est créée, mais au lieu de se voir assigner un nouvel objet créé avec new, elle reçoit une référence existante. Le contenu de la référence x, qui est l'adresse de l'objet sur lequel pointe x, est assigné à y ; et donc x et y sont attachés au même objet. Donc quand on incrémente le i de x dans l'instruction :
t t t
x.i++; t
x.i++
t t t
y’s i will be affected as well. This can be seen in the output:
t le i de y sera modifié lui aussi. On peut le vérifier dans la sortie :
t t t
x: 7 y: 7 Incrementing x x: 8 y: 8 t
x: 7
y: 7
Incrementing x
x: 8
y: 8
t t t
One good solution in this case is to simply not do it: don’t consciously alias more than one reference to an object at the same scope. Your code will be much easier to understand and debug. However, when you’re passing a reference in as an argument—which is the way Java is supposed to work—you automatically alias because the local reference that’s created can modify the “outside object” (the object that was created outside the scope of the method). Here’s an example:
t Une bonne solution dans ce cas est tout simplement de ne pas le faire : ne pas aliaser plus d'une référence à un même objet dans la même portée. Le code en sera d'ailleurs plus simple à comprendre et à débugguer. Cependant, quand on passe une référence en argument - de la façon dont Java est supposé le faire - l'aliasing entre automatiquement en jeu, et la référence locale créée peut modifier « l'objet extérieur » (l'objet qui a été créé en dehors de la portée de la méthode). En voici un exemple :
t t t
//: appendixa:Alias2.java // Method calls implicitly alias their // arguments. public class Alias2 { int i; Alias2(int ii) { i = ii; } static void f(Alias2 reference) { reference.i++; } public static void main(String[] args) { Alias2 x = new Alias2(7); System.out.println("x: " + x.i); System.out.println("Calling f(x)"); f(x); System.out.println("x: " + x.i); } } ///:~ t
//: appendixa:Alias2.java
// Les appels de méthodes aliasent implicitement
// leurs arguments.

public class Alias2 {
  int i;
  Alias2(int ii) { i = ii; }
  static void f(Alias2 reference) {
    reference.i++;
  }
  public static void main(String[] args) {
    Alias2 x = new Alias2(7);
    System.out.println("x: " + x.i);
    System.out.println("Calling f(x)");
    f(x);
    System.out.println("x: " + x.i);
  }
} ///:~
t t t
The output is:
t Le résultat est :
t t t
x: 7 Calling f(x) x: 8 t
x: 7
Calling f(x)
x: 8
t t t
The method is changing its argument, the outside object. When this kind of situation arises, you must decide whether it makes sense, whether the user expects it, and whether it’s going to cause problems.
t La méthode modifie son argument, l'objet extérieur. Dans ce genre de situations, il faut décider si cela a un sens, si l'utilisateur s'y attend, et si cela peut causer des problèmes.
t t t
In general, you call a method in order to produce a return value and/or a change of state in the object that the method is called for. (A method is how you “send a message” to that object.) It’s much less common to call a method in order to manipulate its arguments; this is referred to as “calling a method for its side effects.” Thus, when you create a method that modifies its arguments the user must be clearly instructed and warned about the use of that method and its potential surprises. Because of the confusion and pitfalls, it’s much better to avoid changing the argument.
t En général, on appelle une méthode afin de produire une valeur de retour et/ou une modification de l'état de l'objet sur lequel est appelée la méthode (une méthode consiste à « envoyer un message » à cet objet). Il est bien moins fréquent d'appeler une méthode afin de modifier ses arguments ; on appelle cela « appeler une méthode pour ses effets de bord ». Une telle méthode qui modifie ses arguments doit être clairement documentée et prévenir à propos de ses surprises potentielles. A cause de la confusion et des chausses-trappes engendrés, il vaut mieux s'abstenir de modifier les arguments.
t t t
If you need to modify an argument during a method call and you don’t intend to modify the outside argument, then you should protect that argument by making a copy inside your method. That’s the subject of much of this appendix.
t S'il y a besoin de modifier un argument durant un appel de méthode sans que cela ne se répercute sur l'objet extérieur, alors il faut protéger cet argument en en créant une copie à l'intérieur de la méthode. Cette annexe traite principalement de ce sujet.
t t t

Making local copies

t

Création de copies locales

t t t
To review: All argument passing in Java is performed by passing references. That is, when you pass “an object,” you’re really passing only a reference to an object that lives outside the method, so if you perform any modifications with that reference, you modify the outside object. In addition:
  • Aliasing happens automatically during argument passing.
  • There are no local objects, only local references.
  • References have scopes, objects do not.
  • Object lifetime is never an issue in Java.
  • There is no language support (e.g., “const”) to prevent objects from being modified (that is, to prevent the negative effects of aliasing).
t En résumé : tous les passages d'arguments en Java se font par référence. C'est à dire que quand on passe « un objet », on ne passe réellement qu'une référence à un objet qui vit en dehors de la méthode ; et si des modifications sont faites sur cette référence, on modifie l'objet extérieur. De plus :
  • l'aliasing survient automatiquement durant le passage d'arguments ;
  • il n'y a pas d'objets locaux, que des références locales ;
  • les références ont une portée, les objets non ;
  • la durée de vie d'un objet n'est jamais un problème en Java ;
  • le langage ne fournit pas d'aide (tel que « const ») pour éviter qu'un objet ne soit modifié (c'est à dire pour se prémunir contre les effets négatifs de l'aliasing).
t t t
If you’re only reading information from an object and not modifying it, passing a reference is the most efficient form of argument passing. This is nice; the default way of doing things is also the most efficient. However, sometimes it’s necessary to be able to treat the object as if it were “local” so that changes you make affect only a local copy and do not modify the outside object. Many programming languages support the ability to automatically make a local copy of the outside object, inside the method[79]. Java does not, but it allows you to produce this effect.
t Si on ne fait que lire les informations d'un objet et qu'on ne le modifie pas, la forme la plus efficace de passage d'arguments consiste à passer une référence. C'est bien, car la manière de faire par défaut est aussi la plus efficace. Cependant, on peut avoir besoin de traiter l'objet comme s'il était « local » afin que les modifications apportées n'affectent qu'une copie locale et ne modifient pas l'objet extérieur. De nombreux langages proposent de créer automatiquement une copie locale de l'objet extérieur, à l'intérieur de la méthode  href="#fn79" name="fnB79">[79]. Java ne dispose pas de cette fonctionnalité, mais il permet tout de même de mettre en oeuvre cet effet.
t t t

Pass by value

t

Passage par valeur

t t t
This brings up the terminology issue, which always seems good for an argument. The term is “pass by value,” and the meaning depends on how you perceive the operation of the program. The general meaning is that you get a local copy of whatever you’re passing, but the real question is how you think about what you’re passing. When it comes to the meaning of “pass by value,” there are two fairly distinct camps:
t Ceci nous amène à discuter terminologie, ce qui est toujours bon dans un débat. Le sens de l'expression « passage par valeur » dépend de la perception qu'on a du fonctionnement du programme. Le sens général est qu'on récupère une copie locale de ce qu'on passe, mais cela est tempéré par notre façon de penser à propos de ce qu'on passe. Deux camps bien distincts s'affrontent quant au sens de « passage par valeur » :
t t t
  1. Java passes everything by value. When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference. Ergo, everything is pass-by-value. Of course, the assumption is that you’re always thinking (and caring) that references are being passed, but it seems like the Java design has gone a long way toward allowing you to ignore (most of the time) that you’re working with a reference. That is, it seems to allow you to think of the reference as “the object,” since it implicitly dereferences it whenever you make a method call.
  2. Java passes primitives by value (no argument there), but objects are passed by reference. This is the world view that the reference is an alias for the object, so you don’t think about passing references, but instead say “I’m passing the object.” Since you don’t get a local copy of the object when you pass it into a method, objects are clearly not passed by value. There appears to be some support for this view within Sun, since one of the “reserved but not implemented” keywords was byvalue. (There’s no knowing, however, whether that keyword will ever see the light of day.)
t
  1. Java passe tout par valeur. Quand on passe un scalaire à une méthode, on obtient une copie distincte de ce scalaire. Quand on passe une référence à une méthode, on obtient une copie de la référence. Ainsi, tous les passages d'arguments se font par valeur. Bien sûr, cela suppose qu'on raisonne en terme de références, mais Java a justement été conçu afin de vous permettre d'ignorer (la plupart du temps) que vous travaillez avec une référence. C'est à dire qu'il permet d'assimiler la référence à « l'objet », car il la déréférence automatiquement lorsqu'on fait un appel à une méthode.
  2. Java passe les scalaires par valeur (pas de contestations sur ce point), mais les objets sont passés par référence. La référence est considérée comme un alias sur l'objet ; on ne pense donc pas passer une référence, mais on se dit plutôt « je passe l'objet ». Comme on n'obtient pas une copie locale de l'objet quand il est passé à une méthode, il est clair que les objets ne sont pas passés par valeur. Sun semble plutôt soutenir ce point de vue, puisque l'un des mot-clefs « réservés mais non implémentés » est byvalue (bien que rien ne précise si ce mot-clef verra le jour).
t t t
Having given both camps a good airing, and after saying “It depends on how you think of a reference,” I will attempt to sidestep the issue. In the end, it isn’t that important—what is important is that you understand that passing a reference allows the caller’s object to be changed unexpectedly.
t Après avoir présenté les deux camps et précisé que « cela dépend de la façon dont on considère une référence », je vais tenter de mettre le problème de côté. En fin de compte, ce n'est pas si important que cela - ce qui est important, c'est de comprendre que passer une référence permet de modifier l'objet passé en argument.
t t t

Cloning objects

t

Clonage d'objets

t t t
The most likely reason for making a local copy of an object is if you’re going to modify that object and you don’t want to modify the caller’s object. If you decide that you want to make a local copy, you simply use the clone( ) method to perform the operation. This is a method that’s defined as protected in the base class Object, and which you must override as public in any derived classes that you want to clone. For example, the standard library class ArrayList overrides clone( ), so we can call clone( ) for ArrayList:
t La raison la plus courante de créer une copie locale d'un objet est qu'on veut modifier cet objet sans impacter l'objet de l'appelant. Si on décide de créer une copie locale, la méthode clone() permet de réaliser cette opération. C'est une méthode définie comme protected dans la classe de base Object, et qu'il faut redéfinir comme public dans les classes dérivées qu'on veut cloner. Par exemple, la classe ArrayList de la bibliothèque standard redéfinit clone(),on peut donc appeler clone() sur une ArrayList :
t t t
//: appendixa:Cloning.java // The clone() operation works for only a few // items in the standard Java library. import java.util.*; class Int { private int i; public Int(int ii) { i = ii; } public void increment() { i++; } public String toString() { return Integer.toString(i); } } public class Cloning { public static void main(String[] args) { ArrayList v = new ArrayList(); for(int i = 0; i < 10; i++ ) v.add(new Int(i)); System.out.println("v: " + v); ArrayList v2 = (ArrayList)v.clone(); // Increment all v2's elements: for(Iterator e = v2.iterator(); e.hasNext(); ) ((Int)e.next()).increment(); // See if it changed v's elements: System.out.println("v: " + v); } } ///:~ t
//: appendixa:Cloning.java
// L'opération clone() ne marche que pour quelques
// composants de la bibliothèque Java standard.
import java.util.*;

class Int {
  private int i;
  public Int(int ii) { i = ii; }
  public void increment() { i++; }
  public String toString() {
    return Integer.toString(i);
  }
}

public class Cloning {
  public static void main(String[] args) {
    ArrayList v = new ArrayList();
    for(int i = 0; i < 10; i++ )
      v.add(new Int(i));
    System.out.println("v: " + v);
    ArrayList v2 = (ArrayList)v.clone();
    // Incrémente tous les éléments de v2 :
    for(Iterator e = v2.iterator();
        e.hasNext(); )
      ((Int)e.next()).increment();
    // Vérifie si les éléments de v ont été modifiés :
    System.out.println("v: " + v);
  }
} ///:~
t t t
The clone( ) method produces an Object, which must then be recast to the proper type. This example shows how ArrayList’s clone( ) method does not automatically try to clone each of the objects that the ArrayList contains—the old ArrayList and the cloned ArrayList are aliased to the same objects. This is often called a shallow copy, since it’s copying only the “surface” portion of an object. The actual object consists of this “surface,” plus all the objects that the references are pointing to, plus all the objects those objects are pointing to, etc. This is often referred to as the “web of objects.” Copying the entire mess is called a deep copy.
t La méthode clone() produit un Object, qu'il faut alors retranstyper dans le bon type. Cet exemple montre que la méthode clone() de ArrayList n'essaie pas de cloner chacun des objets que l'ArrayList contient - l'ancienne ArrayList et l'ArrayList clonée référencent les mêmes objets. On appelle souvent cela une copie superficielle, puisque seule est copiée la « surface » d'un objet. L'objet réel est en réalité constitué de cette « surface », plus les objets sur lesquels les références pointent, plus tous les objets sur lesquels ces objets pointent, etc... On s'y réfère souvent en parlant de « réseau d'objets ». On appelle copie profonde le fait de copier la totalité de ce fouillis.
t t t
You can see the effect of the shallow copy in the output, where the actions performed on v2 affect v:
t On peut voir les effets de la copie superficielle dans la sortie, où les actions réalisées sur v2 affectent v :
t t t
v: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] v: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] t
v: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
v: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
t t t
Not trying to clone( ) the objects contained in the ArrayList is probably a fair assumption because there’s no guarantee that those objects are cloneable[80].
t Ne pas essayer d'appeler clone() sur les objets contenus dans l'ArrayList est vraisemblablement une hypothèse raisonnable, car rien ne garantit que ces objets sont cloneables  [80].
t t t

Adding cloneability to a class

t

Rendre une classe cloneable

t t t
Even though the clone method is defined in the base-of-all-classes Object, cloning is not automatically available in every class[81]. This would seem to be counterintuitive to the idea that base-class methods are always available in derived classes. Cloning in Java goes against this idea; if you want it to exist for a class, you must specifically add code to make cloning work.
t Bien que le méthode clone soit définie dans la classe Object, base de toutes les classes, le clonage n'est pas disponible dans toutes les classes  [81]. Cela semble contraire à l'idée que les méthodes de la classe de base sont toujours disponibles dans les classes dérivées. Le clonage dans Java va contre cette idée ; si on veut le rendre disponible dans une classe, il faut explicitement ajouter du code pour que le clonage fonctionne.
t t t

Using a trick with protected

t

Utilisation d'une astuce avec protected

t t t
To prevent default cloneability in every class you create, the clone( ) method is protected in the base class Object. Not only does this mean that it’s not available by default to the client programmer who is simply using the class (not subclassing it), but it also means that you cannot call clone( ) via a reference to the base class. (Although that might seem to be useful in some situations, such as to polymorphically clone a bunch of Objects.) It is in effect a way to give you, at compile-time, the information that your object is not cloneable—and oddly enough most classes in the standard Java library are not cloneable. Thus, if you say:
t Afin d'éviter de rendre chaque classe qu'on crée cloneable par défaut, la méthode clone() est protected dans la classe de base Object. Cela signifie non seulement qu'elle n'est pas disponible par défaut pour le programmeur client qui ne fait qu'utiliser la classe (sans en hériter), mais cela veut aussi dire qu'on ne peut pas appeler clone() via une référence à la classe de base (bien que cela puisse être utile dans certaines situations, comme le clonage polymorphique d'un ensemble d'Objects). C'est donc une manière de signaler, lors de la compilation, que l'objet n'est pas cloneable - et bizarrement, la plupart des classes de la bibliothèque standard Java ne le sont pas. Donc, si on écrit :
t t t
Integer x = new Integer(1); x = x.clone(); t
   Integer x = new Integer(1);
    x = x.clone();
t t t
You will get, at compile-time, an error message that says clone( ) is not accessible (since Integer doesn’t override it and it defaults to the protected version).
t On aura un message d'erreur lors de la compilation disant que clone() n'est pas accessible (puisque Integer ne la redéfinit pas et qu'elle se réfère donc à la version protected).
t t t
If, however, you’re in a class derived from Object (as all classes are), then you have permission to call Object.clone( ) because it’s protected and you’re an inheritor. The base class clone( ) has useful functionality—it performs the actual bitwise duplication of the derived-class object, thus acting as the common cloning operation. However, you then need to make your clone operation public for it to be accessible. So, two key issues when you clone are:
  • Virtually always call super.clone( )
  • Make your clone public
t Si, par contre, on se trouve dans une classe dérivée d'Object (comme le sont toutes les classes), alors on a la permission d'appeler Object.clone() car elle est protected et qu'on est un héritier. La méthode clone() de la classe de base fonctionne - elle duplique effectivement bit à bit l'objet de la classe dérivée, réalisant une opération de clonage classique. Cependant, il faut tout de même rendre sa propre méthode de clonage public pour la rendre accessible. Donc, les deux points capitaux quand on clone sont :
  • Toujours appeler super.clone()
  • Rendre sa méthode clone public
t t t
You’ll probably want to override clone( ) in any further derived classes, otherwise your (now public) clone( ) will be used, and that might not do the right thing (although, since Object.clone( ) makes a copy of the actual object, it might). The protected trick works only once—the first time you inherit from a class that has no cloneability and you want to make a class that’s cloneable. In any classes inherited from your class the clone( ) method is available since it’s not possible in Java to reduce the access of a method during derivation. That is, once a class is cloneable, everything derived from it is cloneable unless you use provided mechanisms (described later) to “turn off” cloning.
t On voudra probablement redéfinir clone() dans de futures classes dérivées, sans quoi le clone() (maintenant public) de la classe actuelle sera utilisé, et pourrait ne pas marcher (cependant, puisque Object.clone() crée une copie de l'objet, ça pourrait marcher). L'astuce protected ne marche qu'une fois - la première fois qu'on crée une classe dont on veut qu'elle soit cloneable héritant d'une classe qui ne l'est pas. Dans chaque classe dérivée de cette classe la méthode clone() sera accessible puisqu'il n'est pas possible en Java de réduire l'accès à une méthode durant la dérivation. C'est à dire qu'une fois qu'une classe est cloneable, tout ce qui en est dérivé est cloneable à moins d'utiliser les mécanismes (décrits ci-après) pour « empêcher » le clonage.
t t t

Implementing the Cloneable interface

t

Implémenter l'interface Cloneable

t t t
There’s one more thing you need to do to complete the cloneability of an object: implement the Cloneable interface. This interface is a bit strange, because it’s empty!
t Il y a une dernière chose à faire pour rendre un objet cloneable : implémenter l'interface Clonable. Cette interface est un peu spéciale, car elle est vide !
t t t
interface Cloneable {} t
interface Cloneable {}
t t t
The reason for implementing this empty interface is obviously not because you are going to upcast to Cloneable and call one of its methods. The use of interface here is considered by some to be a “hack” because it’s using a feature for something other than its original intent. Implementing the Cloneable interface acts as a kind of a flag, wired into the type of the class.
t La raison d'implémenter cette interface vide n'est évidemment pas parce qu'on va surtyper jusqu'à Cloneable et appeler une de ses méthodes. L'utilisation d'interface dans ce contexte est considérée par certains comme une « astuce » car on utilise une de ses fonctionnalités dans un but autre que celui auquel on pensait originellement. Implémenter l'interface Cloneable agit comme une sorte de flag, codé en dur dans le type de la classe.
t t t
There are two reasons for the existence of the Cloneable interface. First, you might have an upcast reference to a base type and not know whether it’s possible to clone that object. In this case, you can use the instanceof keyword (described in Chapter 12) to find out whether the reference is connected to an object that can be cloned:
t L'interface Cloneable existe pour deux raisons. Premièrement, on peut avoir une référence transtypée à un type de base et ne pas savoir s'il est possible de cloner cet objet. Dans ce cas, on peut utiliser le mot-clef instanceof (décrit au chapitre 12) pour savoir si la référence est connectée à un objet qui peut être cloné :
t t t
if(myReference instanceof Cloneable) // ... t
if(myReference instanceof Cloneable) // ...
t t t
The second reason is that mixed into this design for cloneability was the thought that maybe you didn’t want all types of objects to be cloneable. So Object.clone( ) verifies that a class implements the Cloneable interface. If not, it throws a CloneNotSupportedException exception. So in general, you’re forced to implement Cloneable as part of support for cloning.
t La deuxième raison en est qu'on ne veut pas forcément que tous les types d'objets soient cloneables. Donc Object.clone() vérifie qu'une classe implémente l'interface Cloneable, et si ce n'est pas le cas, elle génère une exception CloneNotSupportedException. Donc en général, on est forcé d'implémenter Cloneable comme partie du mécanisme de clonage.
t t t
t t t
t t
    
///
t t t
t
     
Sommaire Le site de Bruce Eckel