equals() ו-hashCode()
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 42 מתוך 87.
בעת השוואת אובייקטים ב-Java, האופרטור == בודק אם שתי הפניות מצביעות לאותו מיקום בזיכרון. כדי להשוות את התוכן בפועל של אובייקטים, צריך לדרוס את המתודה equals().
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}השיטה equals() פועלת לפי דפוס מקובל: תחילה בודקים אם מדובר באותו אובייקט, לאחר מכן מוודאים את הסוג, ולבסוף משווים בין השדות הרלוונטיים.
בכל פעם שדורסים את equals(), חייבים לדרוס גם את hashCode(). זהו חוזה ב-Java: לאובייקטים שווים חייב להיות אותו קוד גיבוב. אוספים מבוססי גיבוב כמו HashMap ו-HashSet מסתמכים על הכלל הזה כדי לפעול כראוי.
@Override
public int hashCode() {
return Objects.hash(name, age);
}שיטת העזר Objects.hash() יוצרת קוד גיבוב מאותם שדות שבהם משתמשת equals(). אם לשני אובייקטים מסוג Person יש אותו שם ואותו גיל, הם ייחשבו שווים וייצרו קודי גיבוב זהים, וכך יובטחו התנהגויות עקביות באוספים.
אתגר
קלבואו נבנה מערכת לניהול רשומות תלמידים שמממשת כראוי שוויון בין אובייקטים. תיצרו מחלקה Student שבה שני תלמידים נחשבים שווים אם יש להם אותו מזהה תלמיד ואותו שם, בלי קשר לשאלה אם הם אותו אובייקט בזיכרון.
תארגנו את הקוד שלכם בשני קבצים:
Student.java: צרו מחלקה שמייצגת תלמיד, עם שלושה שדות פרטיים:studentId(String),name(String), ו-gpa(double). כללו בנאי לאתחול כל השדות ומתודות getter לכל שדה.דרסו את המתודה
equals()כך ששני אובייקטים מסוג Student ייחשבו שווים כאשר יש להם אותוstudentIdוגם אותוname. פעלו לפי התבנית המקובלת: בדקו אם מדובר באותה הפניה, בדקו אם הערך הוא null ואם הטיפוס תואם, ואז השוו בין השדות הרלוונטיים.דרסו את המתודה
hashCode()באמצעותObjects.hash()עם אותם שדות שבהם משתמשים ב-equals(). זכרו לייבא אתjava.util.Objectsבראש הקובץ.Main.java: הפעילו את מחלקת Student שלכם על ידי השוואה בין אובייקטים של תלמידים. תקבלו שש קלטים שמייצגים שני תלמידים: המזהה, השם והממוצע של התלמיד הראשון, ולאחר מכן המזהה, השם והממוצע של התלמיד השני.צרו את שני אובייקטי Student, ואז בצעו שלוש השוואות והדפיסו את התוצאות:
- הדפיסו
Same reference:ולאחר מכן אם התלמיד הראשון שווה לעצמו (התוצאה אמורה להיות true תמיד) - הדפיסו
Content equality:ולאחר מכן אם התלמיד הראשון שווה לתלמיד השני (בהתאם למימוש שלכם של equals) - הדפיסו
Hash codes match:ולאחר מכן אם לשני התלמידים יש אותו קוד גיבוב
- הדפיסו
תקבלו שש קלטים: המזהה של התלמיד הראשון (String), השם של התלמיד הראשון (String), הממוצע של התלמיד הראשון (double), המזהה של התלמיד השני (String), השם של התלמיד השני (String), והממוצע של התלמיד השני (double).
שימו לב שכאשר לשני תלמידים יש מזהים ושמות זהים, גם equals() מחזירה true וגם קודי הגיבוב שלהם זהים. זהו החוזה שעליכם לקיים!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קרא את נתוני הסטודנט הראשון
String id1 = scanner.nextLine();
String name1 = scanner.nextLine();
double gpa1 = scanner.nextDouble();
scanner.nextLine(); // צרוך את תו השורה החדשה
// קרא את נתוני הסטודנט השני
String id2 = scanner.nextLine();
String name2 = scanner.nextLine();
double gpa2 = scanner.nextDouble();
// TODO: צור שני אובייקטים מסוג Student באמצעות נתוני הקלט
// TODO: הדפס "Same reference: " ולאחר מכן האם student1 שווה לעצמו
// TODO: הדפס "Content equality: " ולאחר מכן האם student1 שווה ל-student2
// TODO: הדפס "Hash codes match: " ולאחר מכן האם לשני הסטודנטים יש אותו קוד גיבוב
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותשדות (מאפיינים)מתודת הבנאיהעמסת בנאיםחזרה – מחשבון פשוט4ירושה
ירושה בסיסית (extends)מילת המפתח superדריסת מתודות (@Override)שרשור בנאיםהמחלקה Objectירושה יחידה ורב־רמתיתלמה אין ירושה מרובה של מחלקותסיכום – היררכיית עובדים7מתודות מיוחדות ומחלקת Object
מתודת toString()equals() ו-hashCode()מתודת clone()compareTo() ו-Comparableהממשק Comparatorסיכום – מיון מותאם אישית2מצייני גישה ואנקפסולציה
סקירה של רמות הגישהמתודות Getter ו-Setterהסתרת מידעמילת המפתח finalסיכום – מנהל חשבון בנק5פולימורפיזם
יסודות העמסת מתודותדריסת מתודות (בזמן ריצה)המרה כלפי מעלה וכלפי מטההאופרטור instanceofמחלקות ומתודות מופשטותסיכום – מחשבון צורות8מושגי OOP מתקדמים
הרכבה מול ירושהאגרגציה מול הרכבהמחלקות פנימיות, מקוננות ואנונימיותEnums ומתודות של EnumRecords (Java 16+)מחלקות חתומות (Java 17+)3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומיםתרגלו בעצמכם: קומפיילר Java אונליין