java:equals

Σύγκριση Αντικειμένων

Ο τελεστής ==

Ο βασικός τελεστής της Java για σύγκριση ισότητας είναι ο ==. Όταν χρησιμοποιείται για σύγκριση ισότητας ανάμεσα σε βασικούς (primitive) τύπους όπως int, char κτλ. τότε επιστρέφει true αν οι τιμές προς σύγκριση είναι ίδιες, διαφορετικά false. Για παράδειγμα το παρακάτω θα εκτυπώσει true στην οθόνη,

/* παράδειγμα 1 */
int x = 15;
int y = 14 + 1;
System.out.println(x == y);

Το ερώτημα είναι τι γίνεται όταν χρησιμοποιείται για σύγκριση ανάμεσα σε αντικείμενα:

/* παράδειγμα 2 */
Point p1 = new Point(3, 4);
Point p2 = new Point(3, 4);
System.out.println(p1 == p2);

Στο παραπάνω κομμάτι κώδικα κατασκευάσαμε δύο αντικείμενα τύπου Point. Οι μεταβλητές p1 και p2 περιέχουν αναφορές προς δύο διαφορετικά αντικείμενα. Το γεγονός ότι και τα δύο έχουν τα ίδια χαρακτηριστικά δε σημαίνει ότι πρόκειται για το ίδιο αντικείμενο. Επομένως, αυτός ο κώδικας θα τυπώσει false.

Πότε επιστρέφει true μια τέτοια σύγκριση? Όταν οι μεταβλητές που συγκρίνονται αναφέρονται στο ίδιο ακριβώς αντικείμενο, για παράδειγμα:

/* παράδειγμα 3 */
Point p1 = new Point(3, 4);
Point p2 = p1;
System.out.println(p1 == p2);

Όμως τα σημεία p1 και p2 όπως έχουν οριστεί στο παράδειγμα 2 έχουν τις ίδιες συντεταγμένες και θα θέλαμε να τα θεωρούμε ίσα. Με άλλα λόγια, μας ενδιαφέρει να μπορούμε να συγκρίνουμε δύο αντικείμενα ως προς τα χαρακτηριστικά τους, την κατάστασή τους. Γι αυτό, χρησιμοποιούμε τη μέθοδο

equals

.

Η μέθοδος equals

Η μέθοδος equals παίρνει ως παράμετρο ένα οποιοδήποτε αντικείμενο και επιστρέφει true αν αυτό είναι ίσο με το αντικείμενο πάνω στο οποίο καλείται (το this), διαφορετικά επιστρέφει false:

public boolean equals (Object obj) 

Ακολουθεί ο συμβατικός τρόπος να υλοποιήσουμε την equals. Στο παράδειγμα που ακολουθεί θα θεωρήσουμε ότι η equals ορίζεται στην κλάση Point που έχετε δει στο μάθημα.

  • Ελέγχουμε αν η παράμετρος obj είναι υφιστάμενο αντικείμενο ή όχι. Αν δεν είναι, επιστρέφουμε άμεσα false:
     if (obj == null) {
        return false;
     }
    
  • Ελέγχουμε αν η παράμετρος obj είναι το ίδιο αντικείμενο με το this. Αν είναι, δεν υπάρχει λόγος να κάνουμε περαιτέρω συγκρίσεις.
     if (obj == this) {
        return true;
     }
    
  • Η παράμετρος μπορεί να είναι οποιουδήποτε τύπου, αλλά η σύγκριση έχει νόημα μόνο αν αυτός είναι συμβατός με τον τύπο του τρέχοντος αντικειμένου. Θα δούμε αργότερα τι ακριβώς εννοούμε με το “συμβατός”.
     if (!(obj instanceof Point)) {
        return false;
     }
    
  • Αν έχουμε ξεπεράσει τους παραπάνω ελέγχους, ξέρουμε πια ότι το obj είναι κατ'εξοχήν τύπου Point και μπορούμε να κάνουμε σύγκριση των πεδίων του με τα αντίστοιχα του this. Προσοχή όμως: Επειδή ο compiler βλέπει τύπο Object στη λίστα παραμέτρων, αν γράψουμε obj.x θα παραπονεθεί ότι στην κλάση Object δεν υπάρχει πεδίο με όνομα x. Για να λύσουμε αυτό το πρόβλημα θα φτιάξουμε μια προσωρινή μεταβλητή τύπου Point στην οποία θα αντιγράψουμε το obj με κατάλληλο typecast:
    Point other = (Point)obj;
    return (other.x == this.x && other.y == this.y); 
    

Δεν είναι πάντα απαραίτητο να συγκρίνουμε όλα τα πεδία ένα προς ένα, αλλά μόνο αυτά που θεωρούμε ότι ισότητα μεταξύ τους σημαίνει ισότητα ανάμεσα στα δύο αντικείμενα. Επίσης, αν κάποια πεδία δεν είναι βασικοί τύποι όπως στο παράδειγμα, τότε θα πρέπει να χρησιμοποιήσουμε equals για τη σύγκριση. Δείτε το παράδειγμα εργαστηρίου με την κλάση Deck, όπου τα πεδία είναι τύπου String και τα συγκρίνουμε με equals.

Ο χαρακτηρισμός @Override

Κάθε αντικείμενο που ορίζουμε στη Java είναι τελικά και τύπου Object. Για να είμαστε πιο ακριβείς, κάθε κλάση που ορίζουμε είναι απόγονος της βασικής κλάσης Object. Περισσότερες λεπτομέρειες θα δούμε στην ενότητα για την κληρονομικότητα.

Στην κλάση Object ορίζονται κάποιες μέθοδοι που ισχύουν για όλους τους απογόνους. Ανάμεσα σε αυτές είναι η equals και η toString . Η default λειτουργία της equals είναι να κάνει απλή σύγκριση με ==. Η default λειτουργία της toString είναι να επιστρέφει ένα ειδικό string που χαρακτηρίζει το αντικείμενο.

Αν δεν επαναπροσδιορίσουμε τις υλοποιήσεις αυτών των μεθόδων για τη δική μας κλάση, τότε όποτε τις χρησιμοποιούμε θα εκτελείται ο “default” κώδικας. Αλλά όπως είδαμε ακριβώς πριν, μπορούμε να γράψουμε τι δική μας υλοποίηση της equals για κάθε δική μας κλάση. Όταν κάνουμε κάτι τέτοιο, συνηθίζεται να βάζουμε πριν την υλοποίηση το χαρακτηρισμό @Override ο οποίος “ενημερώνει” τον compiler ότι η παρακάτω μέθοδος υπερισχύει της αντίστοιχης default μεθόδου. Η χρήση του @Override έχει το πλεονέκτημα ότι αν κάνουμε κάποιο λάθος στις παραμέτρους ή στο όνομα ή στον τύπο επιστροφής της μεθόδου, ο compiler θα μας ενημερώσει (θα ανιχνεύσει ότι αυτό που γράφουμε δεν αντιστοιχεί σε κάποια από τις μεθόδους της Object).

java/equals.txt · Last modified: 2017/03/01 15:30 by doufexi