PDA

View Full Version : [java] interfaccia Cloneable


massib86
11-03-2007, 09:49
Non riesco a capire a cosa potrebbe servire l'interfaccia Cloneable!
:cry:
non ha nessun metodo, e inoltre sembra che tutte le classi la estendano!!!
o mi sbaglio??!!:mc:

Mixmar
11-03-2007, 10:09
Come dicono le specifiche, se una classe non estende l'interfaccia "Cloneable", non è possibile invocare il metodo "clone()" della classe Object, che tutti gli oggetti naturalmente ereditano.

Il metodo "clone()" (ma dalla tua domanda penso che tu lo sappia già) serve per tutti i casi in cui si voglia ottenere una "copia di lavoro" di un'oggetto su cui operare senza alterare il contenuto dell'originale: altrimenti, ricordando che ciò che viene passato nelle chiamate ai metodi è un riferimento all'oggetto stesso (salvo il caso dei tipi elementari), qualsiasi azione che ne alteri lo stato risulterà "visibile" anche su tutti i dententori dei riferimenti all'oggetto medesimo, con conseguenze spesso indesiderabili.

Il metodo "clone()" della classe Object inoltre è un metodo generico di copia degli oggetti, e in quanto tale non è adatto a tutte le tipologie di classi che si possono creare: è meglio, in caso di incertezza, fare l'override del metodo clone nella classe che implementa l'interfaccia Cloneable, in maniera che la copia sia "informata" della struttura dell'oggetto che sta copiando e pertanto la procedura sia al contempo corretta e completa.

massib86
11-03-2007, 14:33
ok... ma se devo clonare un oggetto generico? ad esempio :
ho un dato definito così
E elemento1
che viene inizializzato nel main, e devo fare un clone di elemento1 e metterlo in elemento2. Come si fa?
il compilatore mi dice che clone è definito protected in Object (giustamente), ma allora come faccio ad usare clone() su elemento1 ?

Mixmar
12-03-2007, 08:19
ok... ma se devo clonare un oggetto generico? ad esempio :
ho un dato definito così
E elemento1
che viene inizializzato nel main, e devo fare un clone di elemento1 e metterlo in elemento2. Come si fa?
il compilatore mi dice che clone è definito protected in Object (giustamente), ma allora come faccio ad usare clone() su elemento1 ?

Forse il tuo problema è che non conosci il tipo di E a runtime, cioè non sai se E implementi o meno Cloneable?

Oppure "E" è un tipo generico?

massib86
12-03-2007, 09:09
la seconda!... "E" è un tipo generico (i generics...)
Questo problema mi sta facendo impazzire!

Mixmar
12-03-2007, 09:38
Boh... non potresti semplicemente provare a clonare l'oggetto e lanciare la "CloneNotSupportedException" in caso che E non sia Cloneable?

Il fatto è che, o sei sicuro per le tue specifiche che ogni E è Cloneable oppure può darsi che qualche E non lo sia: in tal caso sarebbe corretto informare l'utilizzatore dell'errore. Sul come poi, vedi tu...

massib86
12-03-2007, 10:31
ho già provato a fare così, ma siccome E è generico il compilatore mi da errore dicendomi che E non può utilizzare la clone di Object perchè protected... (il programma non si eseguirà mai così!!)

inoltre se faccio un instanceof Clonable sull'oggetto di tipo E per vedere se implementa l'interfaccia, mi dà sempre una risposta positiva (in quanto l'interfaccia Cloneable non contiene nessun metodo), quindi mi eseguira la clone() anche se è un oggetto che non sovrascrive il metodo!

andbin
12-03-2007, 10:57
siccome E è generico il compilatore mi da errore dicendomi che E non può utilizzare la clone di Object perchè protected... (il programma non si eseguirà mai così!!)Credo che l'unica soluzione sia usare la "reflection". Prendi la classe dell'oggetto "generico", cerchi il metodo "clone" e se c'è lo invochi.

Mixmar
12-03-2007, 11:31
ho già provato a fare così, ma siccome E è generico il compilatore mi da errore dicendomi che E non può utilizzare la clone di Object perchè protected... (il programma non si eseguirà mai così!!)

inoltre se faccio un instanceof Clonable sull'oggetto di tipo E per vedere se implementa l'interfaccia, mi dà sempre una risposta positiva (in quanto l'interfaccia Cloneable non contiene nessun metodo), quindi mi eseguira la clone() anche se è un oggetto che non sovrascrive il metodo!

O fai come ha detto andbin, oppure forse questo codice ti dovrebbe dare, male che vada, una ClassCastException:


elemento2 = (E)((Cloneable)elemento1).clone();


Non sono sicuro che funzioni però... :help:

PGI-Bis
12-03-2007, 13:41
Le classi che concretizzano cloneable non sono poi tantissime. Il fatto che non abbia metodi non significa che tutti quanti siano dei Cloneable. E' un marcatore puro, tipo EventListener.

Dichiara E sottotipo di Cloneable e poi usa la clonazione standard.

massib86
12-03-2007, 15:56
finalmente ho risolto il problema!!
grazie a Andbin ho usato le reflection e tutto si è risolto...
usare il cast a clonable provocava sempre un errore al compilatore in quanto Cloneable non ha alcun metodo (e quindi neanche clone())..

:stordita:

Mixmar
12-03-2007, 16:41
Le classi che concretizzano cloneable non sono poi tantissime. Il fatto che non abbia metodi non significa che tutti quanti siano dei Cloneable. E' un marcatore puro, tipo EventListener.

Dichiara E sottotipo di Cloneable e poi usa la clonazione standard.

[Riflessione Pseudo-filosofica sul linguaggio]
:idea: Quindi si potrebbe considerare "Cloneable" un annotation "sui generis" (mi verrebbe da dire "ante litteram") un po' come il modificatore transient?
[/Riflessione Pseudo-filosofica sul linguaggio]

andbin
12-03-2007, 16:46
Io ho provato a fare così:
import java.lang.reflect.*;
import java.io.*;
import java.util.*;

public class Prova
{
public static void main (String[] args)
{
try
{
// Date e` clonabile
Contenitore<Date> c = new Contenitore<Date> (new Date ());
Contenitore<Date> c2 = (Contenitore<Date>) c.clone ();
}
catch (Exception e)
{
System.out.println (e);
}

try
{
// File non e` clonabile
Contenitore<File> c = new Contenitore<File> (new File ("prova"));
Contenitore<File> c2 = (Contenitore<File>) c.clone ();
}
catch (Exception e)
{
System.out.println (e);
}
}
}


class Contenitore<T> implements Cloneable
{
private T var;

public Contenitore (T var)
{
this.var = var;
}

public Object clone ()
throws CloneNotSupportedException
{
Contenitore<T> newobj = (Contenitore<T>) super.clone ();

if (var != null)
newobj.var = (T) cloneObj (var);

return newobj;
}

private Object cloneObj (Object o)
throws CloneNotSupportedException
{
try
{
Class<?> cl = o.getClass ();
Method m = cl.getMethod ("clone", new Class[0]);
return m.invoke (o, new Object[0]);
}
catch (NoSuchMethodException e)
{
throw new CloneNotSupportedException ();
}
catch (IllegalAccessException e)
{
throw new CloneNotSupportedException ();
}
catch (InvocationTargetException e)
{
throw new CloneNotSupportedException ();
}
}

public T getVar ()
{
return var;
}

public void setVar (T var)
{
this.var = var;
}
}È corretto ciò che ho fatto?? Aspetto conferme (specialmente da PGI-Bis ;) )

massib86
12-03-2007, 18:10
anche secondo me... Cloneable non è altro che una descrizione/proprietà di una classe con la quale si dice che dentro è sovrascritto il metodo clone (messo public anzichè di private). Per il resto l'interfaccia non ha niente dentro; mi viene da pensare che sia molto simile ad una classe astratta!

massib86
12-03-2007, 18:14
Io ho provato a fare così:


È corretto ciò che ho fatto?? Aspetto conferme (specialmente da PGI-Bis ;) )



anche io ho fatto praticamente così, mi sembra la cosa più giusta!!!
;)

PGI-Bis
12-03-2007, 19:56
Potreste usare:

public class Contenitore<T extends Cloneable> implements Cloneable {...

Formalmente questo assicura che la clonazione di T non produca mai una CloneNotSupportedException (ammesso che chi ha scritto la classe T che implementa Cloneable rispetti lo standard della clonazione in Java).

Per invocare un metodo pubblico senza argomenti:

val.getClass().getMethod("clone").invoke(val);

Continuo a preferire il costruttore di copie :D.

massib86
12-03-2007, 22:16
Potreste usare:

public class Contenitore<T extends Cloneable> implements Cloneable {...

Formalmente questo assicura che la clonazione di T non produca mai una CloneNotSupportedException (ammesso che chi ha scritto la classe T che implementa Cloneable rispetti lo standard della clonazione in Java).

Per invocare un metodo pubblico senza argomenti:

val.getClass().getMethod("clone").invoke(val);

Continuo a preferire il costruttore di copie :D.

Si può usare tranquillamente <T extends Cloneable>, ma in questo caso farlo o meno non cambia assolutamente niente visto che il controllo che esista una classe clone sovrascritta lo si fà nella reflection!
Grazie cmq di avermi detto come si invoca il metodo pubblico senza argomenti!
:fagiano:

PGI-Bis
12-03-2007, 23:10
Preciso a scanso di equivoci ma è solo una formalità. L'invocazione riflessiva che ho incollato è, per via dei varargs, uguale a quella di andbin. Non ci sono due modi per invocare un metodo pubblico. Ce n'è uno solo :).

Introducendo un limite nella dichiarazione del tipo generico ottieni un controllo di compatibilità in compilazione. C'è tutta la diatriba sulla tip(azione/izzazione) statica dietro e se andiamo in giro declamando "tanto il controllo lo fa in esecuzione" c'è il rischio che volino coltellate tra le scapole. Io parteggio per il versante dinamico ma occhi aperti che gli statici son agguerriti :D.

massib86
13-03-2007, 08:10
meglio aggiungerlo allora... cosi' il codice mi diventa piu' robusto!!
:eek: