Classe Object Java

utentemain4
Salve, vorrei essere sicuro di aver capito bene il procedimento :).
Firma dei metodi della classe Object:
public String toString()
public boolean equals (Object anObject)
public Object clone()

I metodi possono essere usati sia con l'ereditarietà e sia senza di essa.
In ogni caso, per il metodo clone() bisogna implementare l'interfaccia Cloneable e controllare il lancio dell'eccezione CloneNotSupportedException() con il blocco try-catch.

Vi chiedo di scrivermi il codice per tutti e 3 i metodi, ma in particolare mi serve sapere con precisione il codice dei metodi equals e clone con l'ereditarietà :).

Risposte
apatriarca
L'implementazione dipende ovviamente dalla classe e da quali sono i tuoi obiettivi. Nota che se implementi equals, dovresti anche implementare hashCode. Ti consiglio di dare un occhiata ad esempio a questa pagina (segui anche il link in fondo su hashCode): http://www.javapractices.com/topic/TopicAction.do?Id=17
Leggi inoltre i seguenti articoli che riguardano clone:
http://www.javapractices.com/topic/TopicAction.do?Id=71
http://www.javapractices.com/topic/TopicAction.do?Id=69

utentemain4
Ti ringrazio apatriarca :) ma l'inglese non lo mastico abbastanza bene.
Mi potresti dire tu in linea generale come sovrascrivere il metodo equals() e il metodo clone()?
Quello che devo fare, è un confronto delle variabili di istanza tra due oggetti con il metodo equals (tipo due oggetti studenti "nome, cognome, matricola").
Mentre per clone devo clonare in maniera profonda un oggetto (oggetto studente: nome, cognome, matricola) :)

Questo devo farlo ANCHE con l'ereditarietà :).

Dalle slide della prof, dice che bisogna usare l'interfaccia cloneable per il metodo clone, ma dell'hash code non ne parla O.O (però l'ho letto un po')

apatriarca
In generale il metodo equals deve confrontare lo stato di due istanze dello stesso oggetto e restituire true se devono essere considerate uguali. Il significato vero e proprio di questa affermazione dipende dalla classe e non è più di tanto generalizzabile. Idealmente la relazione data dal metodo dovrebbe essere:
1. Riflessiva. Deve cioè valere a.equals(a) == true.
2. Simmetrica. Deve cioè valere a.equals(b) == b.equals(a).
3. Transitiva. Se valgono a.equals(b) == true e b.equals(c) == true deve valere anche a.equals(c).
In altre parole deve essere una relazione di equivalenza. Deve inoltre essere
4. Consistente. a.equals(b) deve sempre restituire lo stesso valore se a e b sono non nulli e se il loro stato non cambia. In particolare il metodo non deve modificare lo stesso delle due classi.
5. Deve valere a.equals(null) == false per ogni a.
Normalmente viene implementato usando equals (o ==) per ogni campo che determina lo stato della classe. Nel tuo caso, supponendo che tutti e 3 i campi siano di tipo String e che le maiuscole non contino per il nome e il cognome mentre sono importanti per la matricola si avrebbe qualcosa come il seguente:
public class Studente {
    private final String nome;
    private final String cognome;
    private final String matricola;

    public Studente(String nome, String cognome, String matricola) {
        this.nome = nome;
        this.cognome = cognome;
        this.matricola = matricola;
    }

    public final String nome() {
        return nome;
    }


    public final String cognome() {
        return cognome;
    }


    public final String matricola() {
        return matricola;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // test veloce per la proprietà riflessiva

        if (obj == null) return false; // test veloce per l'ultima proprietà

        if (!(obj instanceof Studente)) return false; // non è uno studente..
        
        Studente s = (Studente) obj;
        return this.matricola.equals(s.matricola) && this.cognome.equalsIgnoreCase(s.cognome) && this.nome.equalsIgnoreCase(s.nome);
    }
}

Siccome però la matricola è già normalmente considerata un identificativo per lo studente, si potrebbe supporre di usare quello come unico campo importante per stabilire l'uguaglianza tra le due classi e implementare equals nel modo seguente:
@Override
public boolean equals(Object obj) {
    if (this == obj) return true; // test veloce per la proprietà riflessiva

    if (obj == null) return false; // test veloce per l'ultima proprietà

    if (!(obj instanceof Studente)) return false; // non è uno studente..
        
    Studente s = (Studente) obj;
    return this.matricola.equals(s.matricola);
}


E' infine generalmente necessario implementare anche hashCode. Questo metodo è usato in classi come HashMap e per il corretto funzionamento di queste classi insieme alla tua classe è necessario che hashCode restituisca lo stesso valore per istanze valutate come uguali da equals. Per la classe studente si potrebbe implementare hashCode nel modo seguente (non è certamente l'unico ed è basato sul primo metodo di confronto):
@Override
public int hashCode() {
    return this.matricola.hashCode() ^ this.cognome.toLowerCase().hashCode() ^ this.nome.toLowerCase().hashCode();
}


In linea di massima il metodo equals NON funziona con l'ereditarietà. Supponi di aver infatti implementato una seconda classe StudenteConData che erediti da Studente e con un campo aggiuntivo per la data di nascita per esempio. Supponendo di voler considerare anche questo dato per lo stato, come implementeresti il metodo equals per questa nuova classe figlia? Supponiamo di avere un'istanza s1 di tipo Studente e un'instanza s2 di tipo StudenteConData. Supponiamo che le due classi siano identiche tranne per la data. Quale dovrebbe essere il valore di
s1.equals(s2)

e quale quello di
s2.equals(s1)

? Questo piccolo esempio ti dovrebbe mostrare le difficoltà nell'uso di equals con l'ereditarietà. E' in questi casi preferibile la composizione.

apatriarca
In breve, il metodo copy è da evitare ad ogni costo. Il principale motivo è che è molto difficile implementarlo in modo corretto ed esistono alternative spesso più semplici. Nel primo link che ti avevo mandato c'è un'implementazione del metodo clone. Come c'è scritto nel commento al metodo, per implementare clone si deve:
1. Implementare cloneable
2. Implementare il metodo clone pubblico in modo che lanci l'eccezione CloneNotSupportedException (è importante per le classi figlie che non siano interessate a "farsi clonare".
3. All'interno del metodo richiamare super.clone() e fare il casting verso il tipo della classe (questo metodo si occupa dei campi immutabili senza problemi).
4. Fare una copia esplicita dei campi non immutabili.
5. Tutte le classi figlie devono implementare il metodo clone per non incontrare problemi.
Ti riporto il link con l'implementazione di clone Java Practices -> Avoid clone.

Il metodo più semplice e consigliato è quello di implementare semplicemente il costruttore di copia e usare quello. Se hai bisogno di un esempio chiedi pure.

utentemain4
Ho capito tutto, grazie :D.
Cmq sì, mi servirebbe un esempio per clone() e pure con clone() e l'ereditarietà :).

apatriarca
Nel link che ho riportato è riportata un'implementazione del metodo clone..

utentemain4
Ho visto ma non ho capito bene come funziona, sopratutto per l'ereditarietà.

Cmq per il metodo equals() funziona perfettamente :), tranne che per l'ereditarietà, c'è un errore in una riga di codice ma nn ho capito perché...???
sovrascrittura di equals senza ereditarietà
public class Base  
{ 

	public Base(String n, String c)
	{ 
		this.nome = n;
		this.cognome = c;
	}
	String nome;
	String cognome;
	@Override
	public boolean equals(Object obj)
	{
		if(this == obj) return true; 
		if(obj == null) {
			return false;
		}
		if(!(obj instanceof Base)) {
			return false; 
			} 
		Base b = (Base)obj;
		return this.nome.equals(b.nome) && this.cognome.equalsIgnoreCase(b.cognome);
	}    
	@Override
	public String toString()
	{
		 return nome+cognome;
 
	}
 
	public static void main(String[] args)
	{ 
		Base ba = new Base("pippo","baudo"); 
		Base bb = new Base("pippo","baudo");  
		Base bc = new Base("roger","nave");   
		Basedue d = new Basedue("pippo","baudo");
		Basedue dc = new Basedue("roger","nave");
		
		System.out.println(ba.equals(bb));//true
		System.out.println(ba.equals(bc));//false
		System.out.println(ba.equals(d));//false
		System.out.println(ba.equals(dc));//false
	}

}

//questa è un'altra classe - Basedue
public class Basedue 
{ 
	public Basedue(String n, String c)
	{
		nome = n;
		cognome = c;
	}

	String nome;
	String cognome;
 
	public static void main(String[] args) 
	{ 
		
	}

}


sovrascrittura equals, con ereditarietà
public class Base  extends Basedue
{  
	public Base(String n,String c)
	{
		super(n,c);
	}
	public Base( )
	{ 
		nome = "pietro";
		cognome = "nardi"; 
	}
	String nome;
	String cognome; 
	@Override 
	public boolean equals(Object obj)
	{
		if(obj == null) {
			return false;
		}
		if(!(obj instanceof Basedue)) {
			return false;
		}
		Basedue b = (Basedue) obj;
		return nome.equals(b.nome) && cognome.equalsIgnoreCase(b.cognome); //da errore
	}
	
	public static void main(String[] args)
	{ 
		Base b = new Base("pietro","nardi");
		Basedue c = new Basedue("pietro","nardi");
		System.out.println(b.equals(c)); // da errore 
		 
	}

}


public class Basedue 
{ 
	public Basedue(String n, String c)
	{
		nome = n;
		cognome = c;
	} 
	public Basedue()
	{
		
	}

	String nome;
	String cognome;
}

apatriarca
In che senso da errore? Quale?

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.