إعادة تعريف hashCode و ==
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 71 من 110.
بشكل افتراضي، يكون أي كائنين في لغة Dart متساويين فقط إذا كانا يمثلان نفس المثيل تماماً في الذاكرة. وهذا يعني أن الكائنين اللذين يمتلكان قيماً متطابقة لا يزالان يُعتبران مختلفين:
class Point {
int x, y;
Point(this.x, this.y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - كائنات مختلفة!
}لمقارنة الكائنات حسب قيمها، تحتاج إلى تجاوز كل من عامل == و hashCode. يجب دائماً تجاوز هذين الاثنين معاً - إذا كان كائنان متساويين، فيجب أن يكون لهما نفس رمز التجزئة (hash code):
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // true - نفس القيم!
}يُعد hashCode أمراً بالغ الأهمية لمجموعات مثل مفاتيح Set و Map. تقوم الأداة المساعدة Object.hash() في Dart بدمج قيم متعددة في رمز تجزئة (hash code) واحد. وبدون hashCode صحيح، لن تعمل كائناتك بشكل صحيح في المجموعات القائمة على التجزئة حتى لو تم تجاوز ==.
التحدي
سهلدعنا نبني نظام إحداثيات حيث يمكن مقارنة النقاط بقيمها بدلاً من عناوين ذاكرتها! ستقوم بإنشاء فئة Coordinate تنفذ المساواة القائمة على القيمة بشكل صحيح، مما يجعلها تعمل بشكل صحيح مع فحوصات المساواة والمجموعات القائمة على التجزئة (hash-based collections) مثل Set.
ستقوم بتنظيم الكود الخاص بك في ملفين:
coordinate.dart: أنشئ فئةCoordinateتمثل نقطة في فضاء ثنائي الأبعاد مع حقولfinal int xوfinal int y. قم بعمل override لكل من عامل التشغيل==وhashCodeبحيث يتم اعتبار إحداثيين لهما نفس قيم x و y متساويين. استخدمObject.hash()لإنشاء رمز التجزئة (hash code) من كلا الحقلين.main.dart: استورد ملف الإحداثيات الخاص بك ووضح كيف تغير المساواة القائمة على القيمة سلوك مقارنة الكائنات:- أنشئ كائنين منفصلين من
Coordinate، كلاهما بقيمة x:5و y:10 - اطبع ما إذا كانا متساويين باستخدام
== - اطبع ما إذا كانا متطابقين (نفس المثيل) باستخدام
identical() - أنشئ
Set<Coordinate>وأضف كلا الإحداثيين إليها - اطبع طول المجموعة (يجب أن يكون 1 لأنهما متساويان!)
- أنشئ إحداثياً ثالثاً بقيمة x:
3و y:7، وأضفه إلى المجموعة - اطبع الطول النهائي للمجموعة
- أنشئ كائنين منفصلين من
يوضح هذا التحدي سبب وجوب عمل override لـ hashCode و == معاً دائماً. تستخدم الـ Set رموز التجزئة لتنظيم العناصر، لذا بدون hashCode مناسب، قد يتم تخزين الكائنات المتساوية كنسخ مكررة!
المخرجات المتوقعة:
true
false
1
2ورقة مرجعية
بشكل افتراضي، يكون كائنان في Dart متساويين فقط إذا كانا نفس المثيل في الذاكرة، وليس إذا كان لهما نفس القيم:
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - مثيلات مختلفة!لمقارنة الكائنات حسب قيمها، قم بتجاوز (override) كل من عامل التشغيل == و hashCode. يجب دائمًا تجاوزهما معًا:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}استخدم Object.hash() لدمج قيم متعددة في رمز تجزئة (hash code) واحد. يعد hashCode أمرًا بالغ الأهمية لمجموعات مثل Set و Map - بدونه، لن تعمل الكائنات المتساوية بشكل صحيح في المجموعات القائمة على التجزئة.
للتحقق مما إذا كان كائنان هما نفس المثيل (وليس مجرد قيم متساوية)، استخدم identical():
print(identical(p1, p2)); // false - مثيلات مختلفةجرّب بنفسك
import 'coordinate.dart';
void main() {
// TODO: قم بإنشاء كائنين منفصلين من النوع Coordinate، كلاهما بقيم x: 5 و y: 10
// TODO: اطبع ما إذا كانا متساويين باستخدام ==
// TODO: اطبع ما إذا كانا متطابقين (نفس النسخة) باستخدام ()identical
// TODO: قم بإنشاء Set<Coordinate> وأضف كلا الإحداثيين إليها
// TODO: اطبع طول المجموعة (يجب أن يكون 1 لأنهما متساويان!)
// TODO: قم بإنشاء إحداثي ثالث بقيم x: 3 و y: 7، وأضفه إلى المجموعة
// TODO: اطبع الطول النهائي للمجموعة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)11الدوال الخاصة
إعادة تعريف toString()إعادة تعريف hashCode و ==واجهة Comparableدالة call()إعادة تعريف noSuchMethodمراجعة - Custom Collection3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع