PDA

View Full Version : [Java] Chiarimento copia hard nel costruttore


D4rkAng3l
28-11-2008, 10:50
mmm, la professoressa ha trattato il concetto di copia hard in Java...mi dite se ho capito bene o se mi sfugge qualcosa?

Faccio un esempio pratico: ho una classe chiamata Rettangolo che mi definisce dei rettangoli e delle operazioni su di essi:


public class Rettangolo{

public Punto Pbl, Ptr; // Le variabili di istanza


Gli oggetti di tipo rettangolo sono identificati dalle variabili di istanza:
Pbl: il punto ineriore sinistro
Ptr: il punto superiore destro

in pratica definisco un rettangolo da 2 particolari dei suoi vertici diagonalmente opposti tra loro.

Ovviamente per funzionare la classe rettangolo ha bisogno del tipo di datp Punto definito proprio nella classe Punto:


public class Punto{

private double x,y; // Sono le variabili di istanza che rappresentano le coordinate del punto


/** Costruttore di oggetti istanza della classe Punto date le sue coordinate
@param x di tipo double: l'ascissa del punto da creare
@param y di tipo double: l'ordinata del punto da creare */

public Punto(double x, double y){
this.x = x; // La variabile di istanza x dell'oggetto da creare è ugale al valore del parametro x passato al costruttore
this.y = y; // La variabile di istanza y dell'oggetto da creare è ugale al valore del parametro y passato al costruttore
}


che definisce un oggetto di tipo punto mediante le sue coordinate x ed y.

(in entrambe le classi non inserisco tutti i metodi se no sarebbe lunghissimo)

Se gli oggetti di tipo Punto non sono modificabili (per esempio non posso traslare un punto lungo l'asse x o y)
allora per la classe Rettangolo potrò usare un costruttore banale come


public Retangolo(Punto P1, Punto P2){
this.Pbl = P1;
this.Ptr = P2;
}


che nelle variabili d'istanza di tipo Punto copia i riferimenti ai due punti e quindi il mio rettangolo sarà identificato da due oggetti di tipo Punto precedentemente creati (vabbè in questo caso ho anche semplificato assumendo che i punti passati fossero effettivamente i vertici inferiore sinistro e superiore destro).

Mettiamo ora caso che dentro la mia classe Punto ci voglio mettere due metodi: traslaLungoAsseX() e traslaLungoAsseY() che modificano l'oggetto punto cambiandone nel primo il valore dell'ascissa e nel secondo il valore dell'ordinata.
A questo punto avrei che le variabili di istanza della classe rettangolo (Punto Pbl, Ptr) sono 2 OGGETTI MODIFICABILI e sono nella cacca perchè se nel main traslo un punto che è anche vertice di un rettangolo ne consegue che ho modificato anche il rettangolo che avevo precedentemente creato e ciò potrebbe essere disastroso...allora se le variabili di istanza della classe Rettangolo sono oggetti modificabili devo usare un costruttore che fà la COPIA HARD. Cioè io al costruttore gli passo due oggetti di tipo Punto P1 e P2 e lui invece di assegnare ai vertici Pbl e Ptr il riferimento a P1 e P2 ne fà prima una copia edattribuisce ai miei vertici la copia...così se qualcuno modifica i punti di partenza nel main non va a smerdare anche il rettangolo costruito.

quindi avrò qualcosa del genere come nuovo costruttore sicuro:


public Rettangolo(Punto P1, Punto P2){

if(P1.getAscissa() < P2.getAscissa()){
if(P1.getOrdinata < P2.getOrdinata()){ // Se l'ascissa di P1 è minore dell'ascissa di P2 e se l'ordinata di P1 è minore dell'ordinata di P2
Pbl = new Punto(P1.getAscissa(), P1.getOrdinata());
Ptr = new Punto(P2.getAscissa(), P2.getOrdinata());
}
else{ // Se l'ascissa di P1 è minore dell'ascissa di P2 e se l'ordinata di P1 è maggiore dell'ordinata di P2
Pbl = new Punto(P1.getAscissa(), P2.getOrdinata());
Ptr = new Punto(P2.getAscissa(), P1.getOrdinata());
}
}
else{
if(P1.getOrdinata < P2.getOrdinata){ // Se l'ascissa di P1 è maggiore dell'ascissa di P2 e se l'ordinata di P1 è minore dell'ordinata di P2
Pbl = new Punto(P2.getAscissa, P1.getOrdinata);
Ptr = new Punto(P1.getAscissa, P2.getOrdinata):
}
else{ // Se l'ascissa di P1 è maggiore dell'ascissa di P2 e se l'ordinata di P1 è maggiore dell'ordinata di P2
Pbl = new Punto(P2.getAscissa(), P2.getOrdinata);
Ptr = new Punto(P1.getAscissa(), P1.getOrdinata);
}
}


(In questo caso ho gestito anche le eventualità che i 2 punti passati al costruttore non siano esattamente i vertici inferiore sinistro e superiore destro facendo in modo che se li ricavi e costruisca di conseguenza il rettangolo)

E' correto come ragionamento?

Grazie
Andrea

khelidan1980
28-11-2008, 11:12
non capisco la necessità di traslare i punti,se ti servono altre coordinate non puoi istanziare nuovi punti?

D4rkAng3l
28-11-2008, 11:34
non capisco la necessità di traslare i punti,se ti servono altre coordinate non puoi istanziare nuovi punti?

era solo un esempio della proff per far vedere i problemi con oggetti modificabili, anche lei ci ha detto di usarli il meno possibile ma ci ha fatto vedre come gestirli con la copia hard nel costruttore se proprio dovessero esserci.
Cmq è corretto il mio ragionamento?

banryu79
28-11-2008, 12:14
Sì è corretto.
Ed il motivo è molto semplice: in Java, quando passi come parametri a dei metodi delle variabili non primitive (cioè gli oggetti) in realtà non stai passando l'oggetto vero e proprio ma solo una variabile reference "che punta" a quell'oggetto.

Il che significa che se, dentro a un costruttore di una classe Rettangolo che prende in ingresso due reference a due oggetti Punto, fai così:

class Rettangolo
{
Punto p1; // reference ad un oggetto Punto
Punto p2;
...
public Rettangolo(Punto p1, Punto p2)
{
// copio la reference passata come parametro
// nella reference mantenuta da questa istanza di Rettangolo:
// entrambe le due distinte reference puntano allo stesso oggetto Punto
this.p1 = p1;
this.p2 = p2;
}
}

una modifica esterna del Punto si riflette anche per quell'istanza di Rettangolo.

Se invece fai così:

class Rettangolo
{
Punto p1; // reference ad un oggetto Punto
Punto p2;
...
public Rettangolo(Punto p1, Punto p2)
{
// uso la reference passata come parametro per creare un nuovo
// oggetto Punto che poi "attacco" alla reference mantenuta da questa
// istanza di Rettangolo:
// le due distinte reference puntano a due diversi oggetti Punto
this.p1 = new Punto(p1.x(), p1.y());
this.p2 = new Punto(p2.x(), p2.y());
}
}

una modifica esterna del Punto non tocca l'altro Punto istanziato dentro Rettangolo proprio perchè sono due oggetti diversi.

Ricordati sempre la differenza tra reference e oggetto in Java:

REFERENCE = OGGETTO
Punto reference = new Punto(15, 200);

D4rkAng3l
28-11-2008, 17:51
Ok perfetto...vediamo se ho capito bene tutto il tuo ragionamento :D
Se per esempio volessi inserire nella classe Rettangolo un secondo costruttore che passatogli 4 parametri double x1,y1, x2,y2 che sono le coordinate dei due vertici diagonalmente opposti costruisce un rettangolo, quindi:


/** 2) Costruisce un oggetto di tipo Rettangolo date le coordinate di due suoi punti diagonalmente opposti.
@param x1 di tipo int: è l'ascissa del primo punto
@param y1 di tipo int: è l'ordinata del primo punto
@param x2 di tipo int: è l'ascissa del secondo punto
@param y2 di tipo int: è l'ordinata del secondo punto */

public Rettangolo(double x1, double y1, double x2, double y2){

/* Costruisce un rettangolo date le coordinate dei suoi punti diagonalmente opposti */
Pbl = new Punto(x1,y1);
Ptr = new Punto(x2,y2);
}


Questo costruttore così com'è non dovrebbe creare problemi di sicurezza in quanto io non gli stò passando oggetti modificabili come parametri ma 4 valori di tipo double che sono valori primitivi e lui dentro al costruttore và a creare i 2 oggetti di tipo Punto che attribuirà alle variabili Pbl e Ptr che rappresentano i 2 vertici significativi della mia classe Rettangolo e gli oggetti di tipo Rettangolo che istanzio con questo costruttore non corrono rischi di essere alterati dall'esterno.
Giusto?

Oceans11
28-11-2008, 18:50
Giusto!

D4rkAng3l
28-11-2008, 18:52
Giusto!

mamma mia quanto sono bravooo :D

D4rkAng3l
28-11-2008, 19:06
mmm altro dubio...sulle mie dispense la proff fà tutta una cosa lunga per fare un metodo che restituisce il riferimento al vertice inferiore sinistro di un rettangolo...secondo voi questa mia semplice versione è valida:


public Punto getBottomLeftCorner(){
Punto P = new Punto(Pbl.getAscissa(), Ptr.getOrdinata());
return P;
}


(Ovviamente usando solo il primo costruttore)
Visto che il primo costruttore dati due punti qualsiasi si occupa di costruire un rettangolo definito dal vertice inferiore sinistro (Pbl) e dal vertice superiore destro (Ptr) andandosi a calcolare i corretti Pbl e Ptr.

A questo punto nel mio metodo posso fare una copia hard di Pbl e restituire il riferimento al nuovo punto, ci può stare?

Grazie
Andrea

malocchio
28-11-2008, 21:50
mmm altro dubio...sulle mie dispense la proff fà tutta una cosa lunga per fare un metodo che restituisce il riferimento al vertice inferiore sinistro di un rettangolo...secondo voi questa mia semplice versione è valida:


public Punto getBottomLeftCorner(){
Punto P = new Punto(Pbl.getAscissa(), Ptr.getOrdinata());
return P;
}


(Ovviamente usando solo il primo costruttore)
Visto che il primo costruttore dati due punti qualsiasi si occupa di costruire un rettangolo definito dal vertice inferiore sinistro (Pbl) e dal vertice superiore destro (Ptr) andandosi a calcolare i corretti Pbl e Ptr.

A questo punto nel mio metodo posso fare una copia hard di Pbl e restituire il riferimento al nuovo punto, ci può stare?

Grazie
Andrea

Non vedo il motivo per cui dovresti passare un nuovo oggetto Punto...
Visto che la variabile d'istanza (o meglio l'oggetto a cui punta) non è modificabile, puoi passare direttamente quel riferimento.

ovvero:
return this.Pbl

DanieleC88
28-11-2008, 23:54
mmm altro dubio...sulle mie dispense la proff fà tutta una cosa lunga per fare un metodo che restituisce il riferimento al vertice inferiore sinistro di un rettangolo...secondo voi questa mia semplice versione è valida:
No, se vuoi il vertice inferiore sinistro... Se vuoi quello superiore sinistro, allora sì. ;)

D4rkAng3l
29-11-2008, 10:17
Non vedo il motivo per cui dovresti passare un nuovo oggetto Punto...
Visto che la variabile d'istanza (o meglio l'oggetto a cui punta) non è modificabile, puoi passare direttamente quel riferimento.

ovvero:
return this.Pbl

Punto è un oggetto modificabile perchè come detto nei post precedenti l'esercizio è fatto per avere un metodo traslaLungoX ed un altro metodo traslaLungoY che traslano un punto lungo l'asse X o l'asse Y modificandone le coordinate

D4rkAng3l
29-11-2008, 10:17
No, se vuoi il vertice inferiore sinistro... Se vuoi quello superiore sinistro, allora sì. ;)

mm? che intendi?!?! Pbl contiene il vertice inferiore sinistro e stà per point bottom left :confused:

DanieleC88
29-11-2008, 11:57
mm? che intendi?!?! Pbl contiene il vertice inferiore sinistro e stà per point bottom left :confused:
Appunto:
http://i37.tinypic.com/2ykgkqq.png

Da che coordinate stai creando il nuovo oggetto Punto? Ricontrolla, e guarda che vertice individuano. :)

D4rkAng3l
29-11-2008, 12:00
Appunto:
http://i37.tinypic.com/2ykgkqq.png

Da che coordinate stai creando il nuovo oggetto Punto? Ricontrolla, e guarda che vertice individuano. :)

mmm temo di non capire...ho fatto qualche errore nel costruttore?mi sò perso tra ascisse ed ordinate? dove?

DanieleC88
29-11-2008, 12:04
public Punto getBottomLeftCorner(){
Punto P = new Punto(Pbl.getAscissa(), Ptr.getOrdinata());
return P;
}

Il problema è che prendi l'ascissa di un vertice e l'ordinata dell vertice opposto, come ti ho fatto vedere nel grafico di prima: segui le linee e vedi dove si incrociano! :)

D4rkAng3l
29-11-2008, 12:26
Il problema è che prendi l'ascissa di un vertice e l'ordinata dell vertice opposto, come ti ho fatto vedere nel grafico di prima: segui le linee e vedi dove si incrociano! :)

ahhh sisi è sbagliato giustoma chissene frega...alla fine nel mio programma visto che ho 2 costruttori di cui il secondo prende semplicemente le coordinate di 2 punti per attribuirli ai due punti che definiscono i vertici senza preoccuparsi che siano due vertici Pbl e Ptr (che ora si chiamano semoplicemente punto1 e punto2):


public Rettangolo(double x1, double y1, double x2, double y2){

/* Costruisce un rettangolo date le coordinate dei suoi punti diagonalmente opposti */
punto1 = new Punto(x1,y1);
punto2 = new Punto(x2,y2);
}


allora ho creato un metodo più complesso per restituire il vertice inferiore sinistro che fà tutti i dovuti controllini:


/** Restituisce il riferimento ad un oggetto di tipo Punto corrispondente al vertice dell'angolo inferiore sinistro */

public Punto getBottomLeftCorner(){
double nuovaAscissa, nuovaOrdinata; // Per creare un nuovo punto
Punto P; // Il nuovo punto da restituire

if(punto1.getAscissa()<=punto2.getAscissa() && punto1.getOrdinata()<=punto2.getOrdinata())
P = new Punto(punto1.getAscissa, punto1.getOrdinata);
else if(punto1.getAscissa()>punto2.getAscissa() && punto1.getOrdinata()>punto2.getOrdinata())
P = new Punto(punto1.getAscissa, punto2.getOrdinata);
else{
if(punto1.getAscissa()<=punto2.getAscissa())
nuovaAscissa = punto1.getAscissa();
else nuovaAscissa = punto2.getAscissa();

if(punto1.getOrdinata()<=punto2.getOrdinata())
nuovaOrdinata = punto1.getOrdinata();
else nuovaOrdinata = punto2.getOrdinata();

P = new Punto(nuovaAscissa,nuovaOrdinata);
}

return P; // Ritorna il riferimento al punto creato
}


carino ve? :D

DanieleC88
29-11-2008, 13:32
Non basta scegliere il minimo tra i due sia nel caso dell'ascissa che dell'ordinata?

malocchio
29-11-2008, 17:15
Punto è un oggetto modificabile perchè come detto nei post precedenti l'esercizio è fatto per avere un metodo traslaLungoX ed un altro metodo traslaLungoY che traslano un punto lungo l'asse X o l'asse Y modificandone le coordinate

Ah, OK scusami, spero di non aver creato confusione.
Non avevo capito che ti servisse proprio avere quei 2 metodi. :stordita:

EDIT: stavo pensando, il metodo clone() è considerato sicuro per fare copie hard oppure no?
Cioè un programmatore che vuole fare copia hard nel suo costruttore può usare tranquillamente il clone (sempre che sia implementato, cosa abbastanza rara) oppure preferisce usare un new con i vari get*() ??? Ci si fida oppure no dei clone() scritti da altri?

Penso sia un questione abbastanza teologica... che dicono i padri di Java?

D4rkAng3l
29-11-2008, 17:37
Ah, OK scusami, spero di non aver creato confusione.
Non avevo capito che ti servisse proprio avere quei 2 metodi. :stordita:

EDIT: stavo pensando, il metodo clone() è considerato sicuro per fare copie hard oppure no?
Cioè un programmatore che vuole fare copia hard nel suo costruttore può usare tranquillamente il clone (sempre che sia implementato, cosa abbastanza rara) oppure preferisce usare un new con i vari get*() ??? Ci si fida oppure no dei clone() scritti da altri?

Penso sia un questione abbastanza teologica... che dicono i padri di Java?

non saprei...a noi la proff clone() non ce lo ha mai neanche mensionato..c'è da dire che è il nostro primo esame di java...quindi booo

malocchio
29-11-2008, 17:59
Mi auto-rispondo:
sulle questioni religiose non so, ma ho trovato un pagina interessante riguardo il metodo clone() e i vari problemi che possono nascere con questo:

http://java.sun.com/developer/JDCTechTips/2001/tt0306.html#cloning

banryu79
01-12-2008, 07:53
Per il momento lascia perdene clone() e l'interfaccia Clonable, meglio affrontarli più avanti.
Posto due link interessanti riguardo "il problema" di clone():
- Java Design Issues
A Conversation with Ken Arnold, Part VI (http://www.artima.com/intv/issues3.html)

- Josh Bloch on Design
A Conversation with Effective Java Author, Josh Bloch (http://www.artima.com/intv/bloch13.html)

malocchio
01-12-2008, 21:47
Per il momento lascia perdene clone() e l'interfaccia Clonable, meglio affrontarli più avanti.
Posto due link interessanti riguardo "il problema" di clone():
- Java Design Issues
A Conversation with Ken Arnold, Part VI (http://www.artima.com/intv/issues3.html)

- Josh Bloch on Design
A Conversation with Effective Java Author, Josh Bloch (http://www.artima.com/intv/bloch13.html)

Appena posso ci do un'occhiata, sembrano molto interessanti. Grazie!