Newsgroup: it.comp.java
Titolo: Riutilizzo del codice, ereditarietà con equals()

Sono un novizio di Java e mi sto impratichendo in queste settimane. Ho 
una domanda, forse un po' vaga, da farvi sull'implementazione di 
equals() di una classe che ne estende un'altra che non č Object.
Mi chiedevo se nel ridefinire in una gerarchia di classi il metodo 
equals() di una classe ci fosse un qualche riutilizzo del codice degli 
equals delle classi gerarchicamente piů elevate o meno.
Es. di gerarchia:
Object .> A .> B
Nel ridefinire l'equals() per B sfrutto qualcosa ereditato dall'equals 
di A o di fatto la si riscrive da capo?
Per toString(), ad esempio, č possibile partire nella ridefinizione dal 
toString() di B aggiungendo informazioni alla stringa objA.toString() 
(objA=un'istanza di A). Anche per l'equals() si puň sfruttare come 
inizio l'equals() della superclasse o non si fa?
  ciao e grazie anticipatamente per le risposte
-- 
Google Image Labeler cumulative score: 93140

 
Dovresti dare un occhiata a questo ottimo libro :
 
http://java.sun.com/docs/books/effective/
Contiene proprio un capitolo apposta:
	Item 8: Obey the general contract when overriding equals
-- 
Andrea Francia
http://www.andreafrancia.it/

               |
Cosa fa questo? |
Per quanto riguarda il resto della tua risposta grazie! Ho lavorato cosě 
nelle scorse ore e mi sembrava infatti la strada giusta, ora ne ho la 
conferma.
Un'ultima domanda: com'č che non fai le due classiche versioni 
sovraccaricate di equals() una che accetta un Object come tipo di 
parametro e l'altra che accetta lo stesso tipo della classe in cui 
equals() viene definita? Ho visto che con il sistema da te consigliato 
l'effetto dovrebbe essere comunque lo stesso perň mi ha stupito vedere 
l'approccio differente.
  ciao e grazie


 
L'ho appena addocchiato in biblioteca e devo ringraziarti perchè mi 
sembra veramente moooolto interessante. Fin'ora nello studio dei 
linguaggi che ho affrontato non avevo ancora letto libri che trattassero 
intensivamente casi pratici (studio all'università e per passare l'esame 
non ne ho mai sentito l'esigenza) e ora ne ho riscontrato l'utilità.
Andando OT devo dire che in campo "Linguaggio C" il K&R ha molti più 
casi pratici rispetto allo Schildt (odiato da molti non senza un perchè 
ma che io ho trovato buono per imparare le basi) su cui mi sono 
"formato" ma non ho ancora avuto il tempo di guardarmelo per benino.
Attualmente studio su delle dispense universitarie e sulla 
documentazione Sun di Java ma come riferimento di tanto in tanto 
consulto l'Heckel che però trovo troppo prolisso (secondo me va bene per 
uno che lo studia intensamente come fosse un corso di programmazione ma 
come riferimento, quindi senza averlo necessariamente già studiato 
tutto, non mi sembra tanto adatto...).


 di A o di fatto la si riscrive da capo? 
al limite recuperi l'espressione: 
if(!super.equals(other)) return false;
ma in generale se fai l'override del metodo è perchè vuoi ridefinire la 
logica di equals().
-- 
++ Fino all'anno scorso avevo un solo difetto. Ero presuntuoso.
http://unopuntozero.wordpress.com/
http://www.flickr.com/photos/marko_po/

          }
/me rabbrividisce.
capisco che tu mi abbia killato per non sentirti contraddire, ma a 
beneficio dell'interlocutore dico che ci sono abbondanti motivi per cui 
questa è una cazzata. al limite si usa instanceof che è statico ma almeno 
funziona. confrontare il getClass() è un terno al lotto considerato che 
in molti casi in cui equals() è fondamentale (es. hibernate) *non* sei in 
una istanza della classe che credi di essere.


 una istanza della classe che credi di essere.
Usando JPA con toplink non ho mai riscontrato questo problema.
Puoi spiegarti meglio in modo che ti capisca anche chi non usa Hibernate?
-- 
Andrea Francia
http://andreafrancia.blogspot.com/2008/07/colinux-linux-dentro-windows.html

 
Il super.equals(other) può farti check su campi appartenenti sia alla 
superclasse che alla classe corrente: ho pensato che in questo modo ti 
sgravi di parte della scrittura del codice (in cui dovresti rifare tutti 
quei check copiandoli pedissequamente).
E' questo un comportamento corretto o vale solo per casi particolari (mi 
è appena venuto in mente che se fai dello shadowing in una sottoclasse 
della superclasse poi questo "giochino" potrebbe non funzionare più, 
dovrei rifletterci un attimo...)?


 dovrei rifletterci un attimo...)?
Cos'e' lo shadowing?
Leggiti il libro che ti ho consigliato.


 spiegarti meglio in modo che ti capisca anche chi non usa Hibernate?
tipicamente gli entity restituiti da hibernate sono dei proxy, quindi il 
getClass non restituisce assolutamente la classe che ti aspetti (te ne 
accorgi a tue spese quando li serializzi e dall'altra parte tenti di fare 
il cast al tipo).


 possibilità di ridefinire equals nelle classi figlie.
... e quindi forse la soluzione migliore e' usare
Class.isAssignableFrom(Class) che risolve entrambi i problemi!
--
SoulSpirit

 
Quando "mascheri" (to shadow) una variabile (già definita) ridefinendola 
nello scope in cui ti trovi: tale ridefinizione maschera/nasconde la 
variabile omonima che era già definita. Puoi farlo ad esempio 
all'interno di un blocco se all'esterno di tale blocco avevi già 
definito una variabile con lo stesso nome; puoi farlo anche in una 
sottoclasse (è questo il caso a cui mi riferivo quando parlavo di 
equals() nel caso di shadowing): definisci in una sottoclasse un campo 
con lo stesso nome di un campo della superclasse; nella sottoclasse tale 
campo sarà **logicamente** (=a livello logico) corrispondente al 
corrispettivo campo omonimo della superclasse però puoi implementarlo in 
un modo diverso.
Ad esempio un campo "int k" della superclasse potresti volere 
"mascherarlo" nella sottoclasse perchè il modo in cui è 
definito/implementato non ti soddisfa: lo fai definendo un campo dal 
nome "k" nella sottoclasse e mettiamo che per necessità del problema che 
si sta modellando ti serve che sia ad esempio di tipo "float".
Quando vai a richiamare "k" dal punto della gerarchia in cui hai fatto 
lo "shadowing" in poi, si farà riferimento alla versione "float" di "k".
Se vuoi accedere alla vecchia implementazione del campo lo fai 
riferendoti ad essa con super.k .
Non l'ho mai usato perchè sono pochi giorni che faccio esercizio 
sull'ereditarietà (per la prima volta) e ancora non ne ho avuto bisogno.
Se non sbaglio hai dell'esperienza in Java, mi diresti la tua riguardo 
ciò che ho appena riportato dalle dispense su cui sto studiando? E' un 
comportamento "a rischio"?
Ho appena fatto una ricerca su Google stupito del fatto che una persona 
con esperienza di Java come te non sapesse cos'è una cosa che dovrebbe, 
a giudicare dalle mie dispense, essere una dei concetti basi di Java ma 
Google mi mostra che effettivamente potrebbe essere un termine non poi 
così universalmente adoperato dalla comunità Java. Da una rapida ricerca 
sul testo di Java dell'Heckel (Thinking in Java) vedo che anche li' non 
sarebbe definito questo "shadowing".
  ciao e grazie per l'attenzione


 equals() viene definita? 
Direi che non si possa. Poiche' equals(Object obj) e' un metodo di 
Object, se vuoi fare l'overload del metodo devi per forza usare la 
stessa forma. Ovviamente nulla vieta di fare un methodo
equals(AltraRoba obj)
pero', ad ogni chiamata con un oggetto di tipo diverso da AltraRoba 
interverrebbe sempre equals(Object obj) della superclasse vanificando 
l'overload.
-- 
Dr.Ugo Gagliardelli,Modena,ItalyCertifiedUindoscrasherAñejoAlcoolInside
Spaccamaroni andate a cagare/Spammers not welcome/Spammers vão à merda
Spamers iros a la mierda/Spamers allez vous faire foutre/Spammers loop
schijten/Spammers macht Euch vom Acker/Spamerzy wypierdalac'

 /me rabbrividisce.
Perche' mai? E' perfettamente lecito e non ti impone di aggiungere tutta 
la seie di test instanceof possibili nell'estensione del metodo.
metti:
class Uno {
  @override
  equals(Object obj) {
   return (getClass() == obj.getClass());
  }
}
class Due extends Uno {
  @override
  equals(Object obj) {
   if (super.equals(obj) {
     // specializzo
   }	
  }
}
Sostituendo in Uno
return (obj instanceof Uno.class);
non funzionerebbe se obj e' di tipo Due mentre invece dovrebbe.


 non funzionerebbe se obj e' di tipo Due mentre invece dovrebbe.
non è vero. se obj è di tipo Due è anche instanceof di Uno.


 Class.isAssignableFrom(Class) che risolve entrambi i problemi!
ma anche no, nel senso che non è quello il succo del problema.
se tu hai una gerarchia di classi padre-figlio, se il figlio ha diverse 
proprietà e/o stati è evidente che non è comparabile ne col padre e ne 
con i fratelli. o va bene la comparazione fatta sul padre (tal quale, 
senza estendere il metodo equals() e senza generalizzazione) o altrimenti 
vuol dire che si sta tentando di confrontare pere con le mele.


 classe estesa.
semmai l'override... l'overload è un'altra cosa.


 classe estesa.
Per mrzac: se vuoi saperne di più cerca "java Override annotation".



Tieni presente che lo shadowing è una pratica sconosciuta ai più per
il fatto che è in un certo senso un mal costume, non dovrebbe essere
utilizzata e quindi si preferisce non insegnarla proprio. Questo
perché è difficile capire su quale variabile stai operando, in quanto
hai una variabile per classe nella gerarchia che la definisca. In
generale la soluzione migliore è che le variabili di istanza siano
TUTTE private (una eccezione permessa sono le costanti, ovvero le
static final) e  poi ti giochi il  discorso sul fronte degli accessors
(get set is), Da una parte perché gli accessors sono più flessibili
(ti permettono di impostare permessi di lettura/scrittura in modo
indipendente), potenti (ti permettono di gestire la validazione degli
input, o applicare access restrictions. Ma soprattutto sono più facili
da capire anche nel caso di overriding..
Un altrp es di shadowing, che non riguarda l'ereditarietà è il
seguente:
class XXX {
private int a;
setA(int a){
  a=a;
}
nel metodo chi è a? NORMALMENTE a sarebbe la var di istanza, ma visto
che anche il corpo del metodo ne definisce una, questa copre (di qui
il termine shadowing) quella di istanza. Per la cronaca, il metodo non
fa NULLA, ma è ovvio per un umano capire cosa voleva fare. Per questo
è considerata una pratica pericolosa: il compilatore non ti avvisa del
fatto che non stai facendo nulla nel metodo. Eclipse invece da di
default un warning quando si fanno di queste cose (sia lo shadowing,
sia cose insensate come a=a;)
Un esempio personale: una volta debuggando mi son trovato a cercare di
capire come mai non veniva impostata una variabile e... il debugger mi
mostrava la variabile con lo stesso nome 2 volte. Indovina qual era il
problema? Impostavo la variabile sbagliata..!

 non Ã¨ vero. se obj Ã¨ di tipo Due Ã¨ anche instanceof di Uno.
Non funzionerebbe nel senso che risponde true anche nel caso che dici, 
per cui se volessi accertarmi che le classi siano le stesse dovrei 
reimplementare il metodo in ogni classe che eredita Uno.


 reimplementare il metodo in ogni classe che eredita Uno.
ma infatti se l'uguaglianza semantica non è fattibile dal padre 
confrontando i padri, vuol dire che il padre nemmeno dovrebbe avere una 
sua implementazione di equals(). mentre se è fattibile l'implementazione 
dovrebbe addirittura essere final. tentare di "generalizzare" è un 
tentativo inutile di confontare pere con le mele. 
invece di Uno e Due, prova a pensare ad un esempio pseudo-reale come il 
classico Animale, Gatto e Cane: che senso ha sforzarsi di scrivere un 
equals() generico sulla classe Animale se poi *comunque* Gatto e Cane non 
sono proprio paragonabili? mentre invece se hai una gerarchia tipo Gatto, 
GattoPersiano e GattoSiamese, la logica di confronto è una sola e ha 
senso farla a livello di Gatto (poichè GattoPersiano e GattoSiamese non 
hanno proprietà in più da confrontare).


 
Se Gatto e Cane estendessero Quadrupede sarebbero si' confrontabili ed 
il metodo  Quadrupede.equals() potrebbe tranquillamente fare return 
(getClass() == obj.getClass() senza incidenti neanche sui discendenti.


 (getClass() == obj.getClass() senza incidenti neanche sui discendenti.
dici?
da quella implementazione risulterebbe che tutti i gatti sono uguali fra 
loro e tutti i cani pure. a me non pare tanto corretto, eh...


 
Fai il furbo?
Riprendi il post con Uno e Due, e se hai ancora qualcosa da obiettare fallo.


 fallo.
è da una decina di post che obietto. ma siccome l'opinione è che "faccio 
il furbo" la chiudo qui. sono intervenuto per spiegare quello che ho già 
spiegato, non certo per "vincere" la discussione.


 loro e tutti i cani pure. a me non pare tanto corretto, eh...
Non ho capito bene come Dr.UgoGagliardelli vuole scrivere il codice, ma 
se lo scrive così non è vero che tutti i gatti sono uguali.
class Quadrupede {
   Color furColor;
   public boolean equals(Oject obj) {
     if(obj != null)
       return false;
     if(getClass() != obj.getClass())
       return false;
     Quadrupede other = (Quadrupede) obj;
     if(furColor!=other.furColor)
       return false;
     return true;
   }
}
class Gatto extends Quadrupede {
   public boolean equals(Oject obj) {
     if(!super.equals(obj))   // invoke Quadrupede.equals()
       return false;
     Gatto other = (Gatto) obj;
     if(this.birdsKilledCount != other.birdsKilledCount)
       return false;
     if(this.micesKilledCount != other.micesKilledCount)
       return false;
     return true;
   }
    ...
}


       return false;
Eh?!


Un typo, leggasi == null.


 se lo scrive così non è vero che tutti i gatti sono uguali.
guarda ho capito benissimo cosa intendete (lasciando da parte le 
sfumature). ho già cercato di spiegare più volte perchè, secondo me (e 
non solo), dove non è sbagliato è inutile. non siamo d'accordo, amen. è 
inutile insistere con esempi di dubbio significato per dimostrare che in 
certe condizioni appositamente costruite il "metodo" funziona. 


 certe condizioni appositamente costruite il "metodo" funziona. 
Non so quali esempi siano di dubbio significato secondo te. Se li hai 
già quotati ne abbiamo già parlato, oppure se non ho risposto e perché 
l'osservazione mi sembrava giusta.
Non capisco cosa intendi con "costruire il metodo", se hai già risposto 
in altri punti per me è inutile riparlarne qui.


 in altri punti per me è inutile riparlarne qui.
perfetto.


 
Il fatto che obietti non e' fastidioso. E' fastidioso che obietti come 
se il resto della thread non esista.
E' chiaro che un'obiezione possa funzionare se non e' basata sugli 
stessi presupposti dell'affermazione, ma visto che i presupposti ci 
sono, l'obiezione risulta capziosa.
E' pienamente giustificato che non si capiscano le affermazioni altrui, 
ma se questo e' il caso lo si dice. Da come sono costruite le tue 
argomentazioni, appare, invece, che tu voglia avere ragione a tutti i costi.
Che reazioni ti aspetti?


 
Grazie, ora mi informo.
