web-dev-qa-db-fra.com

À propos de Java cloneable

Je cherchais des tutoriels expliquant à propos de Java Cloneable, mais je n’ai pas de bons liens, et Stack Overflow devient de plus en plus un choix évident.

J'aimerais savoir ce qui suit:

  1. Cloneable signifie que nous pouvons avoir un clone ou une copie d'objets en implémentant l'interface Cloneable. Quels sont les avantages et les inconvénients de cela?
  2. Comment se produit le clonage récursif si l'objet est un objet composite?
91
daydreamer

La première chose que vous devez savoir sur Cloneable est: ne l'utilisez pas.

Il est très difficile d'implémenter le clonage avec Cloneable correct, et l'effort en vaut la peine.

Au lieu de cela, utilisez d'autres options, comme Apache-commons SerializationUtils (deep-clone) ou BeanUtils (shallow-clone), ou utilisez simplement un constructeur de copie.

Voir ici pour les points de vue de Josh Bloch sur le clonage avec Cloneable, ce qui explique les nombreux inconvénients de l’approche. ( Joshua Bloch était un employé de Sun et a dirigé le développement de nombreuses Java.)

151
Bozho

Clonable en soi n'est malheureusement qu'une interface marqueur, c'est-à-dire qu'il ne définit pas la méthode clone ().

Ce qui fait est de changer le comportement de la méthode protégée Object.clone (), qui lève une exception CloneNotSupportedException pour les classes qui n'implémentent pas Cloneable, et effectue une copie superficielle adaptée aux membres pour les classes qui le font.

Même s'il s'agit du comportement que vous recherchez, vous devrez toujours implémenter votre propre méthode clone () afin de le rendre public.

Lors de l’implémentation de votre propre clone (), l’idée est de commencer par l’objet créé par super.clone (), qui a la garantie d’être de la bonne classe, puis de créer une population supplémentaire de champs dans le cas où une copie superficielle n’est pas ce qu’elle est. tu veux. L'appel d'un constructeur à partir de clone () poserait problème, car cela romprait l'héritage dans le cas où une sous-classe voudrait ajouter sa propre logique clonable supplémentaire. s'il appelait super.clone (), il obtiendrait un objet de la mauvaise classe dans ce cas.

Cette approche contourne cependant toute logique pouvant être définie dans vos constructeurs, ce qui pourrait poser problème.

Un autre problème est que toutes les sous-classes qui oublient de remplacer clone () hériteront automatiquement de la copie superficielle par défaut, ce qui ne correspond probablement pas à ce que vous souhaitez en cas d'état mutable (qui sera désormais partagé entre la source et la copie).

La plupart des développeurs n'utilisent pas Cloneable pour ces raisons et implémentent simplement un constructeur de copie.

Pour plus d'informations et les pièges potentiels de Cloneable, je recommande vivement le livre Effective Java de Joshua Bloch

39
Luke Hutteman
  1. Le clonage invoque une manière extra-linguistique de construire des objets - sans constructeur.
  2. Le clonage nécessite que vous traitiez d'une manière ou d'une autre avec CloneNotSupportedException - ou que vous gêniez le code client pour le traiter.
  3. Les avantages sont minimes - vous n'avez simplement pas besoin d'écrire manuellement un constructeur de copie.

Alors, utilisez judicieusement Cloneable. Cela ne vous procure pas suffisamment d'avantages par rapport aux efforts que vous devez faire pour bien faire les choses.

11
Vladimir Ivanov

Le clonage est un paradigme de programmation de base. Le fait que Java l'ait mal implémenté à bien des égards ne diminue en rien le besoin de clonage. Et il est facile d'implémenter un clonage qui fonctionnera comme vous le souhaitez, superficiel Vous pouvez même utiliser le nom clone pour la fonction et ne pas implémenter Cloneable si vous le souhaitez.

Supposons que j'ai les classes A, B et C, où B et C sont dérivés de A. Si j'ai une liste d'objets de type A comme ceci:

ArrayList<A> list1;

Maintenant, cette liste peut contenir des objets de type A, B ou C. Vous ne savez pas quel type sont les objets. Donc, vous ne pouvez pas copier la liste comme ceci:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(new A(a));
}

Si l'objet est réellement de type B ou C, vous n'obtiendrez pas la bonne copie. Et si A est abstrait? Maintenant, certaines personnes ont suggéré ceci:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    if(a instanceof A) {
        list2.add(new A(a));
    } else if(a instanceof B) {
        list2.add(new B(a));
    } else if(a instanceof C) {
        list2.add(new C(a));
    }
}

C'est une très, très mauvaise idée. Que faire si vous ajoutez un nouveau type dérivé? Que se passe-t-il si B ou C sont dans un autre package et que vous n’y avez pas accès dans cette classe?

Ce que vous voudriez faire est ceci:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(a.clone());
}

Beaucoup de gens ont expliqué pourquoi l’implémentation de base du clone Java est problématique. Mais elle est facilement surmontée de cette façon:

En classe A:

public A clone() {
    return new A(this);
}

En classe B:

@Override
public B clone() {
    return new B(this);
}

En classe C:

@Override
public C clone() {
    return new C(this):
}

Je n'implémente pas Cloneable, j'utilise simplement le même nom de fonction. Si vous n'aimez pas cela, nommez-le autrement.

7
Charles

A) Il n'y a pas beaucoup d'avantages à cloner par rapport à un constructeur de copie. Le plus important est probablement la possibilité de créer un nouvel objet du même type dynamique (en supposant que le type déclaré est clonable et qu'il possède une méthode de clonage public).

B) Le clone par défaut crée une copie superficielle et restera une copie superficielle à moins que l'implémentation de votre clone ne la modifie. Cela peut être difficile, surtout si votre classe a des champs finaux

Bozho a raison, un clone peut être difficile à obtenir. Une copie constructeur/usine servira la plupart des besoins.

5
ILMTitan

Quels sont les inconvénients de Cloneable?

Le clonage est très dangereux si l'objet que vous copiez a une composition. Dans ce cas, vous devez envisager un effet secondaire possible, car le clone crée une copie superficielle:

Supposons que vous ayez un objet pour gérer les manipulations liées à la base de données. Disons que cet objet a l'objet Connection en tant que propriété.

Ainsi, lorsque quelqu'un crée un clone de originalObject, l'objet en cours de création crée, disons, cloneObject. Ici, les originalObject et cloneObject contiennent la même référence pour l'objet Connection.

Supposons que originalObject ferme l'objet Connection. Le cloneObject ne fonctionnera donc plus car l'objet connection était partagé entre eux et il était effectivement fermé par le originalObject.

Un problème similaire peut se produire si vous voulez cloner un objet dont IOStream est la propriété.

Comment se produit le clonage récursif si l'objet est un objet composite?

Cloneable effectue une copie superficielle. La signification est que les données de l'objet original et de l'objet clone pointeront vers la même référence/mémoire. Au contraire, dans le cas d’une copie profonde, les données de la mémoire de l’objet original sont copiées dans la mémoire de l’objet clone.

0
027