t
t
t
t
t t A) Passage et Retour d'Objets
tttt
A) Passage et retour d'objets
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

Deep copy via serialization

t

Copie profonde via la sérialisation

t t t
When you consider Java’s object serialization (introduced in Chapter 11), you might observe that an object that’s serialized and then deserialized is, in effect, cloned. t Quand on examine la sérialisation d'objets dans Java (présentée au Chapitre 11), on se rend compte qu'un objet sérialisé puis désérialisé est, en fait, cloné.
t t t
So why not use serialization to perform deep copying? Here’s an example that compares the two approaches by timing them: t Pourquoi alors ne pas utiliser la sérialisation pour réaliser une copie profonde ? Voici un exemple qui compare les deux approches en les chronométrant :
t t t
//: appendixa:Compete.java
import java.io.*;

class Thing1 implements Serializable {}
class Thing2 implements Serializable {
  Thing1 o1 = new Thing1();
}

class Thing3 implements Cloneable {
  public Object clone() {
    Object o = null;
    try {
      o = super.clone();
    } catch(CloneNotSupportedException e) {
      System.err.println("Thing3 can't clone");
    }
    return o;
  }
}

class Thing4 implements Cloneable {
  Thing3 o3 = new Thing3();
  public Object clone() {
    Thing4 o = null;
    try {
      o = (Thing4)super.clone();
    } catch(CloneNotSupportedException e) {
      System.err.println("Thing4 can't clone");
    }
    // Clone the field, too:
    o.o3 = (Thing3)o3.clone();
    return o;
  }
}

public class Compete {
  static final int SIZE = 5000;
  public static void main(String[] args)
  throws Exception {
    Thing2[] a = new Thing2[SIZE];
    for(int i = 0; i < a.length; i++)
      a[i] = new Thing2();
    Thing4[] b = new Thing4[SIZE];
    for(int i = 0; i < b.length; i++)
      b[i] = new Thing4();
    long t1 = System.currentTimeMillis();
    ByteArrayOutputStream buf =
      new ByteArrayOutputStream();
    ObjectOutputStream o =
      new ObjectOutputStream(buf);
    for(int i = 0; i < a.length; i++)
      o.writeObject(a[i]);
    // Now get copies:
    ObjectInputStream in =
      new ObjectInputStream(
        new ByteArrayInputStream(
          buf.toByteArray()));
    Thing2[] c = new Thing2[SIZE];
    for(int i = 0; i < c.length; i++)
      c[i] = (Thing2)in.readObject();
    long t2 = System.currentTimeMillis();
    System.out.println(
      "Duplication via serialization: " +
      (t2 - t1) + " Milliseconds");
    // Now try cloning:
    t1 = System.currentTimeMillis();
    Thing4[] d = new Thing4[SIZE];
    for(int i = 0; i < d.length; i++)
      d[i] = (Thing4)b[i].clone();
    t2 = System.currentTimeMillis();
    System.out.println(
      "Duplication via cloning: " +
      (t2 - t1) + " Milliseconds");
  }
} ///:~
t
//: appendixa:Compete.java
import java.io.*;

class Thing1 implements Serializable {}
class Thing2 implements Serializable {
  Thing1 o1 = new Thing1();
}

class Thing3 implements Cloneable {
  public Object clone() {
    Object o = null;
    try {
      o = super.clone();
    } catch(CloneNotSupportedException e) {
      System.err.println("Thing3 can't clone");
    }
    return o;
  }
}

class Thing4 implements Cloneable {
  Thing3 o3 = new Thing3();
  public Object clone() {
    Thing4 o = null;
    try {
      o = (Thing4)super.clone();
    } catch(CloneNotSupportedException e) {
      System.err.println("Thing4 can't clone");
    }
    // Clone aussi la donnée membre :
    o.o3 = (Thing3)o3.clone();
    return o;
  }
}

public class Compete {
  static final int SIZE = 5000;
  public static void main(String[] args)
  throws Exception {
    Thing2[] a = new Thing2[SIZE];
    for(int i = 0; i < a.length; i++)
      a[i] = new Thing2();
    Thing4[] b = new Thing4[SIZE];
    for(int i = 0; i < b.length; i++)
      b[i] = new Thing4();
    long t1 = System.currentTimeMillis();
    ByteArrayOutputStream buf =
      new ByteArrayOutputStream();
    ObjectOutputStream o =      new ObjectOutputStream(buf);
    for(int i = 0; i < a.length; i++)
      o.writeObject(a[i]);
    // Récupère les copies:
    ObjectInputStream in =      new ObjectInputStream(
        new ByteArrayInputStream(
          buf.toByteArray()));
    Thing2[] c = new Thing2[SIZE];
    for(int i = 0; i < c.length; i++)
      c[i] = (Thing2)in.readObject();
    long t2 = System.currentTimeMillis();
    System.out.println(
      "Duplication via serialization: " +
      (t2 - t1) + " Milliseconds");
    // Maintenant on tente le clonage :
    t1 = System.currentTimeMillis();
    Thing4[] d = new Thing4[SIZE];
    for(int i = 0; i < d.length; i++)
      d[i] = (Thing4)b[i].clone();
    t2 = System.currentTimeMillis();
    System.out.println(
      "Duplication via cloning: " +
      (t2 - t1) + " Milliseconds");
  }
} ///:~
t t t
Thing2 and Thing4 contain member objects so that there’s some deep copying going on. It’s interesting to notice that while Serializable classes are easy to set up, there’s much more work going on to duplicate them. Cloning involves a lot of work to set up the class, but the actual duplication of objects is relatively simple. The results really tell the tale. Here is the output from three different runs: t Thing2 et Thing4 contiennent des objets membres afin qu'une copie profonde soit nécessaire. Il est intéressant de noter que bien que les classes Serializable soient plus faciles à implémenter, elles nécessitent plus de travail pour les copier. Le support du clonage demande plus de travail pour créer la classe, mais la duplication des objets est relativement simple. Les résultats sont édifiants. Voici la sortie obtenue pour trois exécutions :
t t t
Duplication via serialization: 940 Milliseconds
Duplication via cloning: 50 Milliseconds
Duplication via serialization: 710 Milliseconds
Duplication via cloning: 60 Milliseconds
Duplication via serialization: 770 Milliseconds
Duplication via cloning: 50 Milliseconds
t
Duplication via serialization: 940 Milliseconds
Duplication via cloning: 50 Milliseconds

Duplication via serialization: 710 Milliseconds
Duplication via cloning: 60 Milliseconds

Duplication via serialization: 770 Milliseconds
Duplication via cloning: 50 Milliseconds
t t t
Despite the significant time difference between serialization and cloning, you’ll also notice that the serialization technique seems to vary more in its duration, while cloning tends to be more stable. t Outre la différence significative de temps entre la sérialisation et le clonage, vous noterez aussi que la sérialisation semble beaucoup plus sujette aux variations, tandis que le clonage a tendance à être plus stable.
t t t

Adding cloneability further down a hierarchy

t

Supporter le clonage plus bas dans la hiérarchie

t t t
If you create a new class, its base class defaults to Object, which defaults to noncloneability (as you’ll see in the next section). As long as you don’t explicitly add cloneability, you won’t get it. But you can add it in at any layer and it will then be cloneable from that layer downward, like this: t Si une nouvelle classe est créée, sa classe de base par défaut est Object, qui par défaut n'est pas cloneable (comme vous le verrez dans la section suivante). Tant qu'on n'implémente pas explicitement le clonage, celui-ci ne sera pas disponible. Mais on peut le rajouter à n'importe quel niveau et la classe sera cloneable à partir de ce niveau dans la hiérarchie, comme ceci :
t t t
//: appendixa:HorrorFlick.java
// You can insert Cloneability
// at any level of inheritance.
import java.util.*;

class Person {}
class Hero extends Person {}
class Scientist extends Person
    implements Cloneable {
  public Object clone() {
    try {
      return super.clone();
    } catch(CloneNotSupportedException e) {
      // this should never happen:
      // It's Cloneable already!
      throw new InternalError();
    }
  }
}
class MadScientist extends Scientist {}

public class HorrorFlick {
  public static void main(String[] args) {
    Person p = new Person();
    Hero h = new Hero();
    Scientist s = new Scientist();
    MadScientist m = new MadScientist();

    // p = (Person)p.clone(); // Compile error
    // h = (Hero)h.clone(); // Compile error
    s = (Scientist)s.clone();
    m = (MadScientist)m.clone();
  }
} ///:~
t
//: appendixa:HorrorFlick.java
// On peut implémenter le Clonage
// à n'importe quel niveau de la hiérarchie.
import java.util.*;

class Person {}
class Hero extends Person {}
class Scientist extends Person
    implements Cloneable {
  public Object clone() {
    try {
      return super.clone();
    } catch(CloneNotSupportedException e) {
      // Ceci ne devrait jamais arriver :
      // la classe est Cloneable !
      throw new InternalError();
    }
  }
}
class MadScientist extends Scientist {}

public class HorrorFlick {
  public static void main(String[] args) {
    Person p = new Person();
    Hero h = new Hero();
    Scientist s = new Scientist();
    MadScientist m = new MadScientist();

    // p = (Person)p.clone(); // Erreur lors de la compilation
    // h = (Hero)h.clone(); // Erreur lors de la compilation
    s = (Scientist)s.clone();
    m = (MadScientist)m.clone();
  }
} ///:~
t t t
Before cloneability was added, the compiler stopped you from trying to clone things. When cloneability is added in Scientist, then Scientist and all its descendants are cloneable. t Tant que le clonage n'est pas supporté, le compilateur bloque toute tentative de clonage. Si le clonage est ajouté dans la classe Scientist, alors Scientist et tous ses descendants sont cloneables.
t t t

Why this strange design?

t

Pourquoi cet étrange design ?

t t t
If all this seems to be a strange scheme, that’s because it is. You might wonder why it worked out this way. What is the meaning behind this design? t Si tout ceci vous semble étrange, c'est parce que ça l'est réellement. On peut se demander comment on en est arrivé là. Que se cache-t-il derrière cette conception ?
t t t
Originally, Java was designed as a language to control hardware boxes, and definitely not with the Internet in mind. In a general-purpose language like this, it makes sense that the programmer be able to clone any object. Thus, clone( ) was placed in the root class Object, but it was a public method so you could always clone any object. This seemed to be the most flexible approach, and after all, what could it hurt? t Originellement, Java a été conçu pour piloter des boîtiers, sans aucune pensée pour l'Internet. Dans un langage générique tel que celui-ci, il semblait sensé que le programmeur soit capable de cloner n'importe quel objet. C'est ainsi que clone() a été placée dans la classe de base Object, mais c'était une méthode public afin qu'un objet puisse toujours être cloné. Cela semblait l'approche la plus flexible, et après tout, quel mal y avait-il à cela ?
t t t
Well, when Java was seen as the ultimate Internet programming language, things changed. Suddenly, there are security issues, and of course, these issues are dealt with using objects, and you don’t necessarily want anyone to be able to clone your security objects. So what you’re seeing is a lot of patches applied on the original simple and straightforward scheme: clone( ) is now protected in Object. You must override it and implement Cloneable and deal with the exceptions. t Puis, quand Java s'est révélé comme le langage de programmation idéal pour Internet, les choses ont changé. Subitement, des problèmes de sécurité sont apparus, et bien sûr, ces problèmes ont été réglés en utilisant des objets, et on ne voulait pas que n'importe qui soit capable de cloner ces objets de sécurité. Ce qu'on voit donc est une suite de patchs appliqués sur l'arrangement initialement simple : clone() est maintenant protected dans Object. Il faut la redéfinir et implementer Cloneableet traiter les exceptions.
t t t
It’s worth noting that you must use the Cloneable interface only if you’re going to call Object’s clone( ), method, since that method checks at run-time to make sure that your class implements Cloneable. But for consistency (and since Cloneable is empty anyway) you should implement it. t Il est bon de noter qu'on n'est obligé d'utiliser l'interface Cloneable que si on fait un appel à la méthode clone() de Object, puisque cette méthode vérifie lors de l'exécution que la classe implémente Cloneable. Mais dans un souci de cohérence (et puisque de toute façon Cloneable est vide), il vaut mieux l'implémenter.
t t t

Controlling cloneability

t

Contrôler la clonabilité

t t t
You might suggest that, to remove cloneability, the clone( ) method simply be made private, but this won’t work since you cannot take a base-class method and make it less accessible in a derived class. So it’s not that simple. And yet, it’s necessary to be able to control whether an object can be cloned. There are actually a number of attitudes you can take to this in a class that you design: t On pourrait penser que, pour supprimer le support du clonage, il suffit de rendre private la méthode clone(), mais ceci ne marchera pas car on ne peut prendre une méthode de la classe de base et la rendre moins accessible dans une classe dérivée. Ce n'est donc pas si simple. Et pourtant, il est essentiel d'être capable de contrôler si un objet peut être cloné ou non. Une classe peut adopter plusieurs attitudes à ce propos :
t t t
  1. Indifference. You don’t do anything about cloning, which means that your class can’t be cloned but a class that inherits from you can add cloning if it wants. This works only if the default Object.clone( ) will do something reasonable with all the fields in your class.
  2. Support clone( ). Follow the standard practice of implementing Cloneable and overriding clone( ). In the overridden clone( ), you call super.clone( ) and catch all exceptions (so your overridden clone( ) doesn’t throw any exceptions).
  3. Support cloning conditionally. If your class holds references to other objects that might or might not be cloneable (a container class, for example), your clone( ) can try to clone all of the objects for which you have references, and if they throw exceptions just pass those exceptions out to the programmer. For example, consider a special sort of ArrayList that tries to clone all the objects it holds. When you write such an ArrayList, you don’t know what sort of objects the client programmer might put into your ArrayList, so you don’t know whether they can be cloned.
  4. Don’t implement Cloneable but override clone( ) as protected, producing the correct copying behavior for any fields. This way, anyone inheriting from this class can override clone( ) and call super.clone( ) to produce the correct copying behavior. Note that your implementation can and should invoke super.clone( ) even though that method expects a Cloneable object (it will throw an exception otherwise), because no one will directly invoke it on an object of your type. It will get invoked only through a derived class, which, if it is to work successfully, implements Cloneable.
  5. Try to prevent cloning by not implementing Cloneable and overriding clone( ) to throw an exception. This is successful only if any class derived from this calls super.clone( ) in its redefinition of clone( ). Otherwise, a programmer may be able to get around it.
  6. Prevent cloning by making your class final. If clone( ) has not been overridden by any of your ancestor classes, then it can’t be. If it has, then override it again and throw CloneNotSupportedException. Making the class final is the only way to guarantee that cloning is prevented. In addition, when dealing with security objects or other situations in which you want to control the number of objects created you should make all constructors private and provide one or more special methods for creating objects. That way, these methods can restrict the number of objects created and the conditions in which they’re created. (A particular case of this is the singleton pattern shown in Thinking in Patterns with Java, downloadable at www.BruceEckel.com.)
t
  1. L'indifférence. Rien n'est fait pour supporter le clonage, ce qui signifie que la classe ne peut être clonée, mais qu'une classe dérivée peut implémenter le clonage si elle veut. Ceci ne fonctionne que si Object.clone() traite comme il faut tous les champs de la classe.
  2. Implémenter clone(). Respecter la marche à suivre pour l'implémentation de Cloneable et redéfinir clone(). Dans la méthode clone() redéfinie, appeler super.clone() et intercepter toutes les exceptions (afin que la méthode clone() redéfinie ne génère pas d'exceptions).
  3. Supporter le clonage conditionnellement. Si la classe contient des références sur d'autres objets qui peuvent ou non être cloneables (une classe conteneur, par exemple), la méthode clone() peut essayer de cloner tous les objets référencés, et s'ils génèrent des exceptions, relayer ces exceptions au programmeur. Par exemple, prenons le cas d'une sorte d'ArrayList qui essayerait de cloner tous les objets qu'elle contient. Quand on écrit une telle ArrayList, on ne peut savoir quelle sorte d'objets le programmeur client va pouvoir stocker dans l'ArrayList, on ne sait donc pas s'ils peuvent être clonés.
  4. Ne pas implémenter Cloneable mais redéfinir clone() en protected, en s'assurant du fonctionnement correct du clonage pour chacun des champs. De cette manière, toute classe dérivée peut redéfinir clone() et appeler super.clone() pour obtenir le comportement attendu lors du clonage. Cette implémentation peut et doit invoquer super.clone() même si cette méthode attend un objet Cloneable (elle génère une exception sinon), car personne ne l'invoquera directement sur un objet de la classe. Elle ne sera invoquée qu'à travers une classe dérivée, qui, elle, implémente Cloneable si elle veut obtenir le fonctionnement désiré.
  5. Tenter de bloquer le clonage en n'implémentant pas Cloneable et en redéfinissant clone() afin de générer une exception. Ceci ne fonctionne que si toutes les classes dérivées appellent super.clone() dans leur redéfinition de clone(). Autrement, un programmeur est capable de contourner ce mécanisme.
  6. Empêcher le clonage en rendant la classe final. Si clone() n'a pas été redéfinie par l'une des classes parentes, alors elle ne peut plus l'être. Si elle a déjà été redéfinie, la redéfinir à nouveau et générer une exception CloneNotSupportedException. Rendre la classe final est la seule façon d'interdire catégoriquement le clonage. De plus, si on manipule des objets de sécurité ou dans d'autres situations dans lesquelles on veut contrôler le nombre d'objets créés, il faut rendre tous les constructeurs private et fournir une ou plusieurs méthodes spéciales pour créer les objets. De cette manière, les méthodes peuvent restreindre le nombre d'objets créés et les conditions dans lesquelles ils sont créés (un cas particulier en est le patron singleton présenté dans Thinking in Patterns with Java, téléchargeable à www.BruceEckel.com).
t t t
Here’s an example that shows the various ways cloning can be implemented and then, later in the hierarchy, “turned off”: t Voici un exemple qui montre les différentes façons dont le clonage peut être implémenté et interdit plus bas dans la hiérarchie :
t t t
//: appendixa:CheckCloneable.java
// Checking to see if a reference can be cloned.

// Can't clone this because it doesn't
// override clone():
class Ordinary {}

// Overrides clone, but doesn't implement
// Cloneable:
class WrongClone extends Ordinary {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone(); // Throws exception
  }
}

// Does all the right things for cloning:
class IsCloneable extends Ordinary
    implements Cloneable {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone();
  }
}

// Turn off cloning by throwing the exception:
class NoMore extends IsCloneable {
  public Object clone()
      throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
  }
}

class TryMore extends NoMore {
  public Object clone()
      throws CloneNotSupportedException {
    // Calls NoMore.clone(), throws exception:
    return super.clone();
  }
}

class BackOn extends NoMore {
  private BackOn duplicate(BackOn b) {
    // Somehow make a copy of b
    // and return that copy. This is a dummy
    // copy, just to make the point:
    return new BackOn();
  }
  public Object clone() {
    // Doesn't call NoMore.clone():
    return duplicate(this);
  }
}

// Can't inherit from this, so can't override
// the clone method like in BackOn:
final class ReallyNoMore extends NoMore {}

public class CheckCloneable {
  static Ordinary tryToClone(Ordinary ord) {
    String id = ord.getClass().getName();
    Ordinary x = null;
    if(ord instanceof Cloneable) {
      try {
        System.out.println("Attempting " + id);
        x = (Ordinary)((IsCloneable)ord).clone();
        System.out.println("Cloned " + id);
      } catch(CloneNotSupportedException e) {
        System.err.println("Could not clone "+id);
      }
    }
    return x;
  }
  public static void main(String[] args) {
    // Upcasting:
    Ordinary[] ord = {
      new IsCloneable(),
      new WrongClone(),
      new NoMore(),
      new TryMore(),
      new BackOn(),
      new ReallyNoMore(),
    };
    Ordinary x = new Ordinary();
    // This won't compile, since clone() is
    // protected in Object:
    //! x = (Ordinary)x.clone();
    // tryToClone() checks first to see if
    // a class implements Cloneable:
    for(int i = 0; i < ord.length; i++)
      tryToClone(ord[i]);
  }
} ///:~
t
//: appendixa:CheckCloneable.java
// Vérifie si une référence peut être clonée.

// Ne peut être clonée car ne redéfinit pas clone() :
class Ordinary {}

// Redéfinit clone, mais n'implémente pas
// Cloneable :
class WrongClone extends Ordinary {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone(); // Génère une exception
  }
}

// Fait le nécessaire pour le clonage :
class IsCloneable extends Ordinary
    implements Cloneable {
  public Object clone()
      throws CloneNotSupportedException {
    return super.clone();
  }
}

// Interdit le clonage en générant une exception :
class NoMore extends IsCloneable {
  public Object clone()
      throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
  }
}

class TryMore extends NoMore {
  public Object clone()
      throws CloneNotSupportedException {
    // Appelle NoMore.clone(), génère une excpetion :
    return super.clone();
  }
}

class BackOn extends NoMore {
  private BackOn duplicate(BackOn b) {
    // Crée une copie de b d'une façon ou d'une autre
    // et renvoie cette copie. C'est une copie sans
    // intérêt, juste pour l'exemple :
    return new BackOn();
  }
  public Object clone() {
    // N'appelle pas NoMore.clone() :
    return duplicate(this);
  }
}

// On ne peut dériver cette classe, donc on ne peut
// redéfinir la méthode clone comme dans BackOn:
final class ReallyNoMore extends NoMore {}

public class CheckCloneable {
  static Ordinary tryToClone(Ordinary ord) {
    String id = ord.getClass().getName();
    Ordinary x = null;
    if(ord instanceof Cloneable) {
      try {
        System.out.println("Attempting " + id);
        x = (Ordinary)((IsCloneable)ord).clone();
        System.out.println("Cloned " + id);
      } catch(CloneNotSupportedException e) {
        System.err.println("Could not clone "+id);
      }
    }
    return x;
  }
  public static void main(String[] args) {
    // Transtypage ascendant :
    Ordinary[] ord = {
      new IsCloneable(),
      new WrongClone(),
      new NoMore(),
      new TryMore(),
      new BackOn(),
      new ReallyNoMore(),
    };
    Ordinary x = new Ordinary();
    // Ceci ne compilera pas, puisque clone()
    // est protected dans Object:
    //! x = (Ordinary)x.clone();
    // tryToClone() vérifie d'abord si
    // une classe implémente Cloneable :
    for(int i = 0; i < ord.length; i++)
      tryToClone(ord[i]);
  }
} ///:~
t t t
The first class, Ordinary, represents the kinds of classes we’ve seen throughout this book: no support for cloning, but as it turns out, no prevention of cloning either. But if you have a reference to an Ordinary object that might have been upcast from a more derived class, you can’t tell if it can be cloned or not. t La première classe, Ordinary, représente le genre de classes que nous avons rencontré tout au long de ce livre : pas de support du clonage, mais pas de contrôle sur la clonabilité non plus. Mais si on dispose d'une référence sur un objet Ordinary qui peut avoir été transtypé à partir d'une classe dérivée, on ne peut savoir s'il est peut être cloné ou non.
t t t
The class WrongClone shows an incorrect way to implement cloning. It does override Object.clone( ) and makes that method public, but it doesn’t implement Cloneable, so when super.clone( ) is called (which results in a call to Object.clone( )), CloneNotSupportedException is thrown so the cloning doesn’t work. t La classe WrongClone montre une implémentation incorrecte du clonage. Elle redéfinit bien Object.clone() et rend la méthode public, mais elle n'implémente pas Cloneable, donc quand super.clone() est appelée (ce qui revient à un appel à Object.clone()), une exception CloneNotSupportedException est générée et le clonage échoue.
t t t
In IsCloneable you can see all the right actions performed for cloning: clone( ) is overridden and Cloneable is implemented. However, this clone( ) method and several others that follow in this example do not catch CloneNotSupportedException, but instead pass it through to the caller, who must then put a try-catch block around it. In your own clone( ) methods you will typically catch CloneNotSupportedException inside clone( ) rather than passing it through. As you’ll see, in this example it’s more informative to pass the exceptions through. t La classe IsCloneable effectue toutes les actions nécessaires au clonage : clone() est redéfinie et Cloneable implémentée. Cependant, cette méthode clone() et plusieurs autres qui suivent dans cet exemple n'interceptent pasCloneNotSupportedException, mais la font suivre à l'appelant, qui doit alors l'envelopper dans un bloc try-catch. Dans les méthodes clone() typiques il faut intercepter CloneNotSupportedException à l'intérieur de clone() plutôt que de la propager. Cependant dans cet exemple, il est plus intéressant de propager les exceptions.
t t t
Class NoMore attempts to “turn off” cloning in the way that the Java designers intended: in the derived class clone( ), you throw CloneNotSupportedException. The clone( ) method in class TryMore properly calls super.clone( ), and this resolves to NoMore.clone( ), which throws an exception and prevents cloning. t La classe NoMore tente d'interdire le clonage comme les concepteurs de Java pensaient le faire : en générant une exception CloneNotSupportedException dans la méthode clone() de la classe dérivée. La méthode clone() de la classe TryMore appelle super.clone(), ce qui revient à appeler NoMore.clone(), qui génère une exception et empêche donc le clonage.
t t t
But what if the programmer doesn’t follow the “proper” path of calling super.clone( ) inside the overridden clone( ) method? In BackOn, you can see how this can happen. This class uses a separate method duplicate( ) to make a copy of the current object and calls this method inside clone( ) instead of calling super.clone( ). The exception is never thrown and the new class is cloneable. You can’t rely on throwing an exception to prevent making a cloneable class. The only sure-fire solution is shown in ReallyNoMore, which is final and thus cannot be inherited. That means if clone( ) throws an exception in the final class, it cannot be modified with inheritance and the prevention of cloning is assured. (You cannot explicitly call Object.clone( ) from a class that has an arbitrary level of inheritance; you are limited to calling super.clone( ), which has access to only the direct base class.) Thus, if you make any objects that involve security issues, you’ll want to make those classes final. t Mais que se passe-t-il si le programmeur ne respecte pas la chaîne d'appel « recommandée » et n'appelle pas super.clone() à l'intérieur de la méthode clone() redéfinie ? C'est ce qui se passe dans la classe BackOn. Cette classe utilise une méthode séparée duplicate() pour créer une copie de l'objet courant et appelle cette méthode dans clone()au lieu d'appeler super.clone(). L'exception n'est donc jamais générée et la nouvelle classe est cloneable. La seule solution vraiment sûre est montrée dans ReallyNoMore, qui est final et ne peut donc être dérivée. Ce qui signifie que si clone() génère une exception dans la classe final, elle ne peut être modifiée via l'héritage et la prévention du clonage est assurée (on ne peut appeler explicitement Object.clone() depuis une classe qui a un niveau arbitraire d'héritage ; on en est limité à appeler super.clone(), qui a seulement accès à sa classe parente directe). Implémenter des objets qui traite de sujets relatifs à la sécurité implique donc de rendre ces classes final.
t t t
The first method you see in class CheckCloneable is tryToClone( ), which takes any Ordinary object and checks to see whether it’s cloneable with instanceof. If so, it casts the object to an IsCloneable, calls clone( ) and casts the result back to Ordinary, catching any exceptions that are thrown. Notice the use of run-time type identification (see Chapter 12) to print the class name so you can see what’s happening. t La première méthode qu'on voit dans la classe CheckCloneable est tryToClone(), qui prend n'importe quel objet Ordinary et vérifie s'il est cloneable grâce à instanceof. Si c'est le cas, il transtype l'objet en IsCloneable, appelle clone() et retranstype le résultat en Ordinary, interceptant toutes les exceptions générées. Remarquez l'utilisation de l'identification dynamique du type (voir Chapitre 12) pour imprimer le nom de la classe afin de suivre le déroulement du programme.
t t t
In main( ), different types of Ordinary objects are created and upcast to Ordinary in the array definition. The first two lines of code after that create a plain Ordinary object and try to clone it. However, this code will not compile because clone( ) is a protected method in Object. The remainder of the code steps through the array and tries to clone each object, reporting the success or failure of each. The output is: t Dans main(), différents types d'objets Ordinary sont créés et transtypés en Ordinary dans la définition du tableau. Les deux premières lignes de code qui suivent créent un objet Ordinary et tentent de le cloner. Cependant ce code ne compile pas car clone() est une méthode protected dans Object. Le reste du code parcourt le tableau et essaye de cloner chaque objet, reportant le succès ou l'échec de l'opération. Le résultat est :
t t t
Attempting IsCloneable
Cloned IsCloneable
Attempting NoMore
Could not clone NoMore
Attempting TryMore
Could not clone TryMore
Attempting BackOn
Cloned BackOn
Attempting ReallyNoMore
Could not clone ReallyNoMore
t
Attempting IsCloneable
Cloned IsCloneable
Attempting NoMore
Could not clone NoMore
Attempting TryMore
Could not clone TryMore
Attempting BackOn
Cloned BackOn
Attempting ReallyNoMore
Could not clone ReallyNoMore
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel