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);
}
}تتبع method equals() نمطًا قياسيًا: first تحقّق مما إذا كان هو نفس الكائن، ثم تحقّق من النوع، وأخيرًا نفّذ compare للحقول ذات الصلة.
عندما تتجاوز equals()، يجب عليك أيضًا تجاوز hashCode(). هذا عقد في Java: يجب أن يكون للكائنات المتساوية رمز hash نفسه. تعتمد المجموعات القائمة على hash، مثل 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نفسها AND قيمةnameنفسها. اتبع النمط القياسي: تحقّق من المرجع نفسه، ثم تحقّق من القيمة Null والنوع، وبعد ذلك قارن الحقول ذات الصلة.تجاوز طريقة
hashCode()باستخدامObjects.hash()مع الحقول نفسها المستخدمة فيequals(). تذكّر استيرادjava.util.Objectsفي أعلى ملفك.Main.java: اجعل فئة Student تعمل من خلال مقارنة كائنات الطلاب. ستتلقى ستة إدخالات تمثّل طالبين: معرّف الطالب الأول واسمه وGPA الخاص به، متبوعةً بمعرّف الطالب الثاني واسمه وGPA الخاص به.أنشئ كائني Student، ثم أجرِ ثلاث مقارنات واطبع النتائج:
- اطبع
Same reference:متبوعًا بما إذا كان الطالب الأول يساوي نفسه (يجب أن يكون ذلك true دائمًا) - اطبع
Content equality:متبوعًا بما إذا كان الطالب الأول يساوي الطالب الثاني (استنادًا إلى تطبيقك لـ equals) - اطبع
Hash codes match:متبوعًا بما إذا كان لدى الطالبين رمز hash نفسه
- اطبع
ستتلقى ستة إدخالات: معرّف الطالب الأول (String)، واسم الطالب الأول (String)، وGPA الطالب الأول (double)، ومعرّف الطالب الثاني (String)، واسم الطالب الثاني (String)، وGPA الطالب الثاني (double).
لاحظ أنه عندما يكون لدى طالبين معرّفا ID واسما matching، فإن equals() لكليهما تُرجع true AND تتطابق رموز hash الخاصة بهما. هذا هو العقد الذي يجب أن تحافظ عليه!
جرّب بنفسك
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: Print "Same reference: " followed by whether student1 equals itself
// TODO: Print "Content equality: " followed by whether student1 equals student2
// TODO: Print "Hash codes match: " followed by whether both students have the same hash code
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت