CodeGym /مدونة جافا /Random-AR /يساوي الطريقة في جافا: أفضل الممارسات
John Squirrels
مستوى
San Francisco

يساوي الطريقة في جافا: أفضل الممارسات

نشرت في المجموعة
أهلاً! سنتحدث اليوم عن طريقتين مهمتين في Java: equals() و hashCode() . هذه ليست المرة الأولى التي نلتقي بهم: تبدأ دورة CodeGym بدرس قصير حول يساوي() — اقرأه إذا نسيته أو لم تراه من قبل... يساوي وطرق hashCode: أفضل الممارسات - 1في درس اليوم، نحن' سأتحدث عن هذه المفاهيم بالتفصيل. وصدقوني، لدينا شيء لنتحدث عنه! ولكن قبل أن ننتقل إلى الجديد، دعونا نقوم بتحديث ما قمنا بتغطيته بالفعل :) كما تتذكر، عادة ما تكون فكرة مقارنة كائنين باستخدام عامل التشغيل == فكرة سيئة، لأن == يقارن بين المراجع. هذا هو مثالنا مع السيارات من درس حديث:
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
إخراج وحدة التحكم:

false
يبدو أننا أنشأنا كائنين متطابقين في السيارة : قيم الحقول المقابلة لكائني السيارة هي نفسها، لكن نتيجة المقارنة لا تزال خاطئة. نحن نعرف السبب بالفعل: تشير مراجع car1 و car2 إلى عناوين ذاكرة مختلفة، لذا فهما غير متساويين. لكننا ما زلنا نريد المقارنة بين الشيئين، وليس مرجعين. أفضل حل لمقارنة الكائنات هو طريقة يساوي () .

يساوي () الأسلوب

ربما تتذكر أننا لا ننشئ هذه الطريقة من الصفر، بل نتجاوزها: يتم تعريف طريقة يساوي () في فئة الكائن . ومع ذلك، في شكله المعتاد، فهو قليل الفائدة:
public boolean equals(Object obj) {
   return (this == obj);
}
هذه هي الطريقة التي يتم بها تعريف طريقة يساوي () في فئة الكائن . هذه مقارنة بين المراجع مرة أخرى. لماذا جعلوها هكذا؟ حسنًا، كيف يعرف منشئو اللغة أي الكائنات في برنامجك تعتبر متساوية وأيها ليست كذلك؟ :) هذه هي النقطة الرئيسية في طريقة يساوي () — منشئ الفئة هو الذي يحدد الخصائص المستخدمة عند التحقق من مساواة كائنات الفئة. ثم تقوم بتجاوز طريقة يساوي () في صفك. إذا كنت لا تفهم تمامًا معنى "يحدد الخصائص"، فلنأخذ مثالاً. إليك فئة بسيطة تمثل الرجل: Man.
public class Man {

   private String noseSize;
   private String eyesColor;
   private String haircut;
   private boolean scars;
   private int dnaCode;

public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
   this.noseSize = noseSize;
   this.eyesColor = eyesColor;
   this.haircut = haircut;
   this.scars = scars;
   this.dnaCode = dnaCode;
}

   // Getters, setters, etc.
}
لنفترض أننا نكتب برنامجًا يحتاج إلى تحديد ما إذا كان شخصان توأمًا متطابقًا أم مجرد متشابهين. لدينا خمس خصائص: حجم الأنف، ولون العين، ونمط الشعر، ووجود ندوب، ونتائج اختبار الحمض النووي (للتبسيط، نمثل ذلك كرمز صحيح). أي من هذه الخصائص تعتقد أنها ستسمح لبرنامجنا بالتعرف على التوائم المتماثلة؟ يساوي وطرق hashCode: أفضل الممارسات - 2وبطبيعة الحال، فإن اختبار الحمض النووي فقط يمكن أن يوفر الضمان. يمكن أن يكون لدى شخصين نفس لون العين، وقصة الشعر، والأنف، وحتى الندوب - هناك الكثير من الناس في العالم، ومن المستحيل ضمان عدم وجود أي شبيهين هناك. لكننا بحاجة إلى آلية موثوقة: فقط نتيجة اختبار الحمض النووي هي التي ستسمح لنا بالتوصل إلى نتيجة دقيقة. ماذا يعني هذا بالنسبة لطريقتنا equals()؟ نحن بحاجة إلى تجاوزه في Manالفصل، مع مراعاة متطلبات برنامجنا. يجب أن تقارن الطريقة int dnaCodeمجال الكائنين. إذا كانت متساوية، فإن الكائنات متساوية.
@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
هل هو حقا بهذه البساطة؟ ليس حقيقيًا. لقد أغفلنا شيئًا ما. بالنسبة لأشياءنا، حددنا حقلًا واحدًا فقط ذا صلة بتحقيق المساواة بين الكائنات: dnaCode. تخيل الآن أنه ليس لدينا حقل واحد، بل 50 حقلاً ذا صلة. وإذا كانت جميع الحقول الخمسين لكائنين متساوية، فإن الكائنين متساويان. مثل هذا السيناريو ممكن أيضا. المشكلة الرئيسية هي أن تحقيق المساواة من خلال مقارنة خمسين مجالاً هي عملية تستغرق وقتاً طويلاً وتستهلك موارد كثيرة. الآن تخيل أنه بالإضافة إلى Manفصلنا، لدينا Womanفصل يحتوي على نفس الحقول الموجودة في Man. إذا استخدم مبرمج آخر دروسنا، فيمكنه بسهولة كتابة تعليمات برمجية مثل هذا:
public static void main(String[] args) {

   Man man = new Man(........); // A bunch of parameters in the constructor

   Woman woman = new Woman(.........); // The same bunch of parameters.

   System.out.println(man.equals(woman));
}
في هذه الحالة، التحقق من قيم الحقل لا معنى له: يمكننا أن نرى بسهولة أن لدينا كائنات من فئتين مختلفتين، لذلك لا توجد طريقة يمكن أن تكون متساوية! هذا يعني أنه يجب علينا إضافة فحص إلى equals()الطريقة، ومقارنة فئات الكائنات المقارنة. من الجيد أننا فكرنا في ذلك!
@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
لكن ربما نسينا شيئًا آخر؟ حسنًا... على الأقل، يجب علينا التحقق من أننا لا نقارن كائنًا بنفسه! إذا كان المرجعان A وB يشيران إلى نفس عنوان الذاكرة، فهما نفس الكائن، ولا نحتاج إلى إضاعة الوقت ومقارنة 50 حقلاً.
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
كما أنه لا يضر إضافة علامة اختيار إلى null: لا يمكن أن يكون أي كائن مساويًا لـ null. لذا، إذا كانت معلمة الطريقة فارغة، فلا فائدة من إجراء فحوصات إضافية. مع أخذ كل هذا في الاعتبار، تبدو equals()طريقتنا للفصل Manكما يلي:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
نقوم بإجراء كافة الفحوصات الأولية المذكورة أعلاه. في نهاية اليوم إذا:
  • نحن نقارن كائنين من نفس الفئة
  • والكائنات المقارنة ليست نفس الكائن
  • والكائن الذي تم تمريره ليس كذلكnull
...ثم ننتقل إلى مقارنة الخصائص ذات الصلة. بالنسبة لنا، هذا يعني dnaCodeمجالات الجسمين. عند تجاوز equals()الطريقة، تأكد من مراعاة هذه المتطلبات:
  1. الانعكاسية.

    عند equals()استخدام الطريقة لمقارنة أي كائن بنفسه، يجب أن يُرجع صحيحًا.
    لقد امتثلنا بالفعل لهذا المطلب. طريقتنا تشمل:

    if (this == o) return true;

  2. تناظر.

    إذا a.equals(b) == true، فلا b.equals(a)بد من العودة true.
    طريقتنا تلبي هذا المطلب أيضًا.

  3. عبورية.

    إذا كان هناك كائنان يساويان كائنًا ثالثًا، فيجب أن يكونا متساويين لبعضهما البعض.
    إذا a.equals(b) == trueو a.equals(c) == true، b.equals(c)فيجب أيضًا أن يعود صحيحًا.

  4. إصرار.

    يجب أن تتغير نتيجة equals()فقط عند تغيير الحقول المعنية. إذا لم تتغير بيانات الكائنين، فيجب equals()أن تكون النتيجة هي نفسها دائمًا.

  5. عدم المساواة مع null.

    بالنسبة لأي كائن، a.equals(null)يجب إرجاع خطأ.
    هذا ليس مجرد مجموعة من "التوصيات المفيدة"، ولكنه عقد صارم منصوص عليه في وثائق أوراكل

الجزء 2: طريقة HashCode - أفضل الممارسات
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION