تعلّم البرمجة بلغة كوتلن (35): التحميل الزائد أو إعادة التعريف Overloading

تعلّم البرمجة بلغة كوتلن (35): التحميل الزائد أو إعادة التعريف Overloading
أستمع الى المقال

شرحنا في الدروس السابقة، الدوال وكيفية الإعلان عنها واستخداماتها المختلفة. ثم استعرضنا مفهوم الدوال الملحقة، التي يمكننا إلحاقها بأصناف تم إنشاؤها مسبقًا.

في هذا الدرس، نغوص أكثر في الاستفادة من استخدام الدوال في شفرتنا، عبر دراسة مفهوم التحميل الزائد Overloading. والذي يمكننا من إعادة تعريف نفس الدالة، وترك المترجم Compiler، يقرر ما هي الدالة المناسبة لكل استدعاء.

ما هو التحميل الزائد Overloading:

لا يمكن الإعلان عن أكثر من دالة  بنفس الاسم والمعاملات Parameters (عددها وأنواع بياناتها)، حتى ولو اختلف نوع البيانات الذي ترجعه الدالة ReturnType. لأن المترجم Compiler، لا يأخذ نوع بيانات النتيجة التي تسترجعها الدالة كدليل إختلاف بين الدوال.

لنتمكن من استخدام نفس الاسم مع عدة دوال، يجب أن تكون معاملات الدوال مختلفة في عددها ونوع بياناتها. وهو ما يعرف بالتحميل الزائد Overloading أو إعادة تعريف الدالة بنفس الاسم.

إنشاء دالتين بنفس الاسم:

إذا كان لدينا دالة:

fun functionName(variableName: VariableType): ReturnType {

}

ثم إحتجنا لإنشاء دالة أخرى بنفس الاسم، يجب أن نغير توقيعها:

fun functionName(variableName: AnotherVariableType): ReturnType {

}

بالرغم من أن الدالة الثانية لديها نفس اسم الأولى، ولكن عند الاستدعاء، سيتعرف مترجم كوتلن على الدالة التي تم استدعاؤها منهما، عبر القيم Arguments المرسلة للدالة. سيتضح هذا الأمر أكثر عبر الأمثلة في الفقرة أدناه.

الفرق بين تغيير الاسم أو المعامل:

يمكننا بالطبع تغيير اسم الدالة وسيتعرف المترجم على كل دالة من اسمها. إذًا ما هي الحاجة لعمل تحميل زائد للدوال؟ حسنًا، للإجابة على هذا السؤال، دعونا ننظر للمثال التالي:

الشفرة أعلاه، هي دالة لجمع عددين صحيحين من النوع Int، وإعادة نتيجة من النوع Int أيضًا. لذلك اسميناها addInt لأنه اسم يناسب عملها. وعند إرسال عددين من النوع Int، ستعمل الدالة على جمع العددين، وإعادة نتيجة الجمع:

ولأنه يجب أن نوفر لمستخدم البرنامج عمليات الجمع للأعداد الكسرية Double أيضًا. لذلك سننشئ دالة أخرى لجمع الكسور، ونختار اسم مناسب addDouble:

هي الأخرى ستقوم بعملها وتجمع الأعداد الكسرية المرسلة إليها كقيم.

أما إذا أراد المستخدم جمع أعداد صحيحة أكبر من أن يتحملها نوع الـ Int. مثل: 2147483647 + 2147483647. إذا استخدمنا دالة ()addInt ستحدث نتيجة غير متوقعة. (شرحنا هذا في درس أنواع العدد). لذا نحتاج دالة ثالثة بنوع بيانات عددي، يتحمل أعداد أكبر من Int وهو النوع Long:

والآن علينا استدعاء الدالة المناسبة لكل عملية:

بالرغم من صحة هذه الطريقة، ولكنها ستقودنا في النهاية إلى البحث عن الأسماء المناسبة لكل دالة. مما سيؤدي بنا إلى تسمية دوالنا أسماء عشوائية قد لا تكون واضحة لنا في المستقبل. بالتالي نكون قد قضينا على قابلية القراءة لشفرتنا.

أما إذا اتبعنا طريقة التحميل الزائد للدوال، سنترك إسم الدالة كما هو ونغير معاملاتها فقط:

كما يظهر في الصورة أعلاه، تم استدعاء الدالتين المناسبتين لكل استدعاء، لذلك تم تلوين أسماءهما باللون الأصفر. ولم يتم استدعاء الدالة التي بها معاملات الـ Int، لأنه لايوجد استدعاء به قيم من النوع Int. ولذلك يظهر اسمها باللون الرمادي في برنامج IntelliJ، وهي طريقة البرنامج في إخبارنا بأن هذه الدالة غير مستخدمة. مع العلم أنه تم استدعاء الدالة التي تحمل معاملات من نوع البيانات Long، لأننا عند الاستدعاء وضعنا حرف (L) بعد العدد الصحيح.

وهذا حدث بالرغم من الدوال الثلاث لديهم نفس الاسم. وهو بالضبط ما سيفعله مترجم كوتلن عند تنفيذ البرنامج. سيستدعي الدالة المناسبة حسب نوع القيم المدخلة من المستخدم. وهكذا نكون قد استفدنا من طريقة الـ Overloading، في عدم البحث عن أسماء عند كل إنشاء لدالة. وإعادة استخدام نفس الإسم، يجعل من عمل الدوال أوضح بكثير.

الـ Overloading وقيم المعاملات الإفتراضية Default Arguments:

غالبًا ما تستخدم التحميل الزائد، لغات البرمجة التي لا تدعم ميزة قيم المعاملات الافتراضية، لتحاكي هذه الميزة. أما في كوتلن، في بعض الأحيان يكون استخدام قيم المعاملات الافتراضية، منطقيًا أكثر من استخدام الـ Overloading.

فمثلًا، إذا كنا نريد تمكين المستخدم من جمع أكثر من عددين صحيحين من النوع Int في المثال أعلاه:

وعند استدعاء الدالتين، ستعملان بشكل عادي. ولكن ستحدث مشكلة إذا أراد المستخدم جمع ليس عددين أو أربعة، بل ثلاث أعداد صحيحة:

كما نرى في الصورة، يظهر خط أحمر تحت دالة ()add، عند استدعائها وإرسال ثلاث قيم لها، للدلالة على أنه لا يوجد دالة ()add تستقبل ثلاث قيم بالضبط. والآن يمكننا إضافة دالة تستقبل ثلاث أعداد صحيحة، مما يؤدي إلى تكرار الشفرة، أو الاستفادة من ميزة القيم الافتراضية:

قللنا الشفرة كثيرًا وتم حلّ المشكلة أيضًا:

الـ Overloading والدوال الملحقة:

يُعد الـ Overloading مفيدًا أيضًا عندما نريد إضافة دالة لصنف لا نملك تغيير شفرته، ولديه دالة تحمل نفس الإسم، فيما يعرف بالدوال الملحقة. ولمزيد من الفهم، دعونا نعيد كتابة شفرتنا من الدرس السابق:

الصنف A، لديه دالة عضو اسمها member. ثم ألحقنا به دالة بنفس الاسم. وعند فعل التالي:

سيتم طباعة hi from member مباشرةً، وتجاهل الدالة الملحقة. وهذا لأن مترجم كوتلن، سيعطي الأولوية دائمًا للدالة العضو عند تشابه الأسماء.

لنضمن استدعاء دالتنا الملحقة الملحقة بالصنف، علينا أن نغير معاملاتها:

fun A.member(s: String) = println(s)

والآن عند الاستدعاء، علينا ارسال قيمة نصية String للدالة:

في هذه الحالة سيستدعي المترجم الدالة الملحقة، لأنها هي التي لديها معامل نصي من النوع String، بخلاف الدالة العضو التي ليس لديها أي معامل.

استخدام الـ Overloading في مكتبة كوتلن القياسية:

استخدم مطورو لغة كوتلن، هذه الطريقة في العديد من الدوال في المكتبة القياسية. من ضمنها دالة الطباعة ()println، والتي استخدمناها كثيرًا منذ بداية هذه الدورة. 

لرؤية تعريفات هذه الدالة، يمكننا الضغط على ctrl في لوحة المفاتيح، ثم الضغط على اسم الدالة بزر الفأرة الأيسر في نفس الوقت، في برنامج IntelliJ. ليتم فتح الملف Console.kt الذي يحتويها:

ما يظهر في الصورة، هو بعض تعريفاتها لبعض أنواع البيانات الأساسية. ولكن عمومًا توجد 11 دالة باسم ()println في هذا الملف. تطبع كل منها نوع معين من أنواع البيانات الأساسية وغيرها.

وهذا بالتحديد هو السبب الذي يجعلنا نستدعي الدالة ونرسل لها القيمة التي نريد طباعتها، ليقوم مترجم كوتلن باستدعاء الدالة المناسبة حسب كل قيمة. أما لو كانت هذه الدالة بأسماء مختلفة، لواجهتنا مشكلة حفظ اسم كل دالة مناسبة لكل نوع بيانات نريد طباعته.

الخلاصة:

نستخدم ميزة التحميل الزائد مع الدوال، لأن هذا في كثير من الأحيان، يؤدي إلى زيادة قابلية القراءة للشفرة. فلا يجب أن نكرر أسماء الدوال التي تؤدي نفس المهمة، مثل أن نقول: ()addInt أو ()addDouble. فهذا تكرار لا داعي له، وسيكون من الأوضح تغيير المعاملات وترك إسم الدالة، لأن كلا الدالتين تقومان بنفس المهمة.

وفي حالات أخرى، يكون من المنطقي أكثر الاستفادة من ميزة القيم الافتراضية في كوتلن بدلًا عن التحميل الزائد. قلا يجب أن نكتب دوال كثيرة تجمع الأعداد الصحيحة مثلًا، إذا كان يمكننا فعل ذلك عبر استخدام القيم الافتراضية.

التفريق بين الحالتين ومتى يتم يستخدم هذا أو ذاك، يعود بالدرجة الأولى إلى مدى خبرة المبرمج بعد استخدامه الميزتين لفترة مناسبة. ولكن أيضًا يمكننا وضع سؤال في أحد مواقع مساعدة المبرمجين للحصول على مساعدة من المبرمجين ذوي الخبرة. مثل موقع stackoverflow.com، والذي توجد به إجابة مناسبة لموضوع درسنا في هذا الرابط.

هذا الدرس هو جزء من سلسلة تعليم مبادئ البرمجة بلغة كوتلن. لمُتابعة الدروس منذ البداية ومُشاهدة فهرس المحتويات يمكنك الانتقال إلى الدرس الأول من هنا.

هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.