2010-03-25 3 views
2

La méthode Object.clone() dans Java est assez spéciale, car au lieu de renvoyer une copie de l'objet qui doit être cloné avec le type Object, elle renvoie le type Object correct. Cela peut être mieux décrit avec le code suivant: AlorsEst-il possible en Java d'implémenter quelque chose de similaire à Object.clone()?

class A implements Cloneable 
{ 
    public Object clone() throws CloneNotSupportedException { 
     return super.clone(); 
    } 
} 

class B extends A { 
} 

public class MainABC { 
    public static void main(String[] args) throws CloneNotSupportedException { 
     B b = new B(); 
     B b1 = (B)b.clone(); //see here that we are using A's .clone(). The only 
          //thing it does is call Object's clone(). 
     System.out.println(b1.getClass()); //but as we see here, its not an Object 
              //its a B! 
    } 
} 

, pourrait expliquer, si possible, quelqu'un s'il est de toute façon de reproduire ce qui se passe à l'intérieur de la méthode de Object.clone()?

+1

Qu'essayez-vous d'accomplir? Si ce n'est que du code, écrivez-le explicitement. –

+0

Ce code ne compile même pas et n'a aucun sens. Vérifiez la réponse de Savvas Dalkitsis. Vous devez clarifier davantage ce que vous souhaitez accomplir exactement. – BalusC

+0

Doit faire plus de sens maintenant .. –

Répondre

2

Il est certainement vrai que Object.clone() ne fait quelques choses qui ne peuvent simplement pas être réalisés en Java.

De Josh Bloch on Design: Copy Constructor versus Cloning (Souligné par l'auteur):

méthode clone de l'objet est très délicat. C'est basé sur des copies de terrain, et c'est "extra-linguistique".Il crée un objet sans appeler un constructeur. Il n'y a aucune garantie qu'il préserve les invariants établis par les constructeurs.

Object.clone() fait quelque chose qui n'est pas censé être autorisé par le langage. C'est pourquoi, parmi beaucoup d'autres raisons, clone() est cassé.

(Si vous n'avez pas déjà, vous devriez aussi lire son livre Effective Java, de comprendre pourquoi il (et bien d'autres) pensent que Java et de clone()Cloneable est cassé).


Si vous voulez juste créer un objet de la même classe qu'un autre objet quelconque, alors c'est en fait tout à fait réalisable, avec une mise en garde (à savoir que tous les types sont accessibles au public instanciable) en utilisant la réflexion.

Voici un exemple de comment utiliser la réflexion pour:

  • Découvrez la classe d'un objet à l'exécution
  • Liste de ses champs déclarés, les méthodes et les constructeurs
  • Trouver son constructeur de copie (le cas échéant) et tente de l'appeler en utilisant l'objet donné comme paramètre .

import java.lang.reflect.*; 

public class NewInstance { 
    static void print(String label, Object[] arr) { 
     System.out.println(label); 
     for (Object o : arr) { 
     System.out.println(o); 
     } 
     System.out.println("---"); 
    } 

    static Object newInstance(Object o) { 
     Class<?> c = o.getClass(); 
     System.out.println("Class is " + c); 
     print("FIELDS:", c.getDeclaredFields()); 
     print("METHODS:", c.getDeclaredMethods()); 
     print("CONSTRUCTORS:", c.getDeclaredConstructors()); 

     try { 
     Constructor<?> cc = c.getDeclaredConstructor(c); 
     o = cc.newInstance(o); 
     } catch (NoSuchMethodException e) { 
     System.out.println("No copy constructor found!"); 
     } catch (IllegalAccessException e) { 
     System.out.println("Copy constructor inaccessible!"); 
     } catch (InstantiationException e) { 
     System.out.println("Instantiation failed!"); 
     } catch (InvocationTargetException e) { 
     System.out.println("Copy constructor threw " + e.getCause()); 
     } 
     return o; 
    } 

    public static void main(String args[]) { 
     Object o1 = "hello"; 
     Object o2 = newInstance(o1); 
     boolean success = (o1 != o2) && (o1.equals(o2)); 
     System.out.println("Attempt " + (success ? "succeeded!" : "failed :(")); 
    } 
} 

Sortie:

Class is class java.lang.String 
FIELDS: 
// (omitted) 
METHODS: 
// (omitted) 
CONSTRUCTORS: 
public java.lang.String() 
public java.lang.String(java.lang.String) // this is what we're looking for! 
// (rest omitted) 
--- 
Attempt succeeded! 

Notez que ceci est juste un exemple pour montrer le type peut être inspecté au moment de l'exécution et un constructeur de copie peut être recherchée et invoqué. Tel quel, cela ne fonctionne pas si o est un ArrayList, car il n'a aucun constructeur qui prend un ArrayList (il en a un qui prend un Collection, qu'un ArrayList est).

Je vais vous laisser un exercice sur la façon d'étendre la recherche du constructeur de copie pour inclure ces surcharges compatibles.

1

Je n'ai jamais entendu ou vu une construction de langage qui vous offre la fonctionnalité que clone vous donne gratuitement.

Vous pouvez l'imiter, mais je ne crois pas que vous pouvez reproduire le comportement.

+0

Avooiding appelant un constructeur sur le nouvel objet ne volera pas avec le bytecode vérifiable (la sérialisation est mise en œuvre avec la génération de bytecode cassée). –

0

Je pense que vous n'avez même pas testé le code que vous avez tapé ici!

Si vous essayez de compiler ce code, vous obtenez des erreurs. Tout d'abord le retour super.clone() vous donne une erreur de "Type discordance: ne peut pas convertir de l'objet à A"

Deuxièmement (je suppose que c'était un type de faute) vous n'avez pas créé une instance de B. Vous dit

B = new B(); 

Même si nous changeons cela à

B b = new B(); 
B b1 = b.clone(); 

vous obtiendrez une erreur parce que b.clone() retourne une instance de la classe A.

Alors désolé, mais vous ne décrire pas arriver ... Vous avez besoin d'un casting pour obtenir ce que vous voulez.

Donc, pour résumer:

public class A extends Object { 
    public A clone() { 
     return super.clone(); // Error "Type mismatch: cannot convert from Object to A" 
    } 

    public static void main(String[] args) { 
     B b = new B(); 
     B b1 = b.clone(); // Error "Type mismatch: cannot convert from A to B" 
    } 
} 

class B extends A { 
} 

EDIT: Je crains que vous l'avez mal à nouveau. Ce que vous avez fait renvoie une instance de B parce que vous l'avez convertie en instance B. Il retournerait B même si vous lancez quelque chose de complètement différent ... Par exemple

B b = (B)(new JLabel()); 
System.out.println(b.class); 

Ce imprimera la classe de B. En fait, il imprimerait la classe B si jamais il est arrivé là ... Vous obtiendra une exception avant d'y arriver ... Pour avoir ce que vous voulez, vous devez remplacer manuellement la méthode clone et fournir votre propre implémentation. Votre question n'est pas valide du tout .. Vous devriez le supprimer mais vous ne pouvez pas puisque vous avez des réponses upvoted ... Je voterais pour fermer au moins ...

+0

Ouais je l'ai tapé sans vérifier et ai tout complètement faux: P semble être correct maintenant. –

+0

Je pense que vous avez encore tort ... vérifier ma réponse éditée ... –

+0

Vérifiez ce code: Objet abc = (Object) new Entier (5); System.out.println (abc.getClass()); Il va imprimer Integer, parce que c'est ce que l'objet sur le tas est vraiment.Donc, dans mon code, il imprime B non pas à cause de la distribution (c'est quelque chose dont seul le compilateur se soucie ..) mais parce que l'objet à l'exécution est de type B! –

1

Le Objenesis library peut être utilisé pour créer des instances de classes arbitraires même si elles n'ont pas de constructeur sans-args. Il utilise différentes astuces pour chaque JVM pour accomplir cela. Vous pouvez l'utiliser avec un peu de code de réflexion pour copier toutes les valeurs de champ de l'objet source vers la destination.

0

Que pensez-vous de cela?

public class A { 
    public static void main(String[] args) { 
     B b = new B(); 
     B b1 = (B)b.getNew(); 
     System.out.println(b1.getClass()); 
    } 

    public Object getNew() { 
     try { 
      return getClass().newInstance(); 
     } catch (InstantiationException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 
} 

class B extends A { 

} 
+0

Toujours n'est pas la même chose .. Lire la publication de polygene. –

Questions connexes