تعلّم البرمجة بلغة كوتلن (37): أصناف الثوابت التعددية Enum

تعلّم البرمجة بلغة كوتلن (37): أصناف الثوابت التعددية Enum
أستمع الى المقال

تحدثنا في قسم مقدمة في الكائنات، عن ما هي الكائنات Objects في البرمجة، وكيفية إنشاء أصناف Classes خاصة بنا في كوتلن. وشرحنا أيضًا، كيف يمكننا تخزين مجموعة غير محدّدة العدد من الكائنات، في تجميعة Collection، أو مصفوفة Array.

في هذا الدرس، سنستعرض صنف خاص، يسهل لنا تجميع مجموعة من الكائنات، والتي تكون ثابتة العدد والقيم بطبيعتها. وهو صنف الثوابت Constants التعددية Enum.

ماهو الـ Enum:

كلمة enum هي اختصار لكلمة enumeration والتي تعني تعداد. وهو في كوتلن، صنف خاص يمكننا من تخزين مجموعة من الثوابت في مكان واحد والتعامل معها جميعًا في نفس الوقت. وهو بالتالي، يجعل من شفرتنا أكثر وضوحًا ويزيد قابلية قراءتها.

تهيئة الـ Enum:

نعلن عن الصنف enum، باستخدام الكلمتين المفتاحيتين enum class:

enum class ClassName {

    // هنا نضع الثوابت التي نريد تخزينها في هذا الصنف

}

فمثلًا، إذا أردنا التعامل مع الألوان التي يتكون منها قوس قزح Rainbow، سيكون استخدام الصنف enum مثاليًا لحفظ بيانات هذه الألوان. لأنه كما نعرف، أن عددها ثابت 7، وقيمها أيضًا ثابتة: الأحمر والبرتقالي والأصفر والأخضر والأزرق والنيلي والبنفسجي:

enum class Rainbow {

    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET

}

نلاحظ أنه وضعنا أسماء الألوان بالحروف الإنجليزية الكبيرة Uppercase، وفصلنا بينها بفاصلة ( , ). هذه هي الطريقة المتّبعة لكتابة أسماء الكائنات (الثوابت) داخل صنف enum.

كل ما يمكن تعداده، يمكن حفظه في صنف enum:

بالإضافة إلى ذلك، يمكننا بالطبع إنشاء صنف enum لكائنات أخرى. مثل: 

أيام الإسبوع:

enum class Week {

    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY

}

نوع الجنس:

enum class Gender {

    MALE, FEMALE

}

مقاسات البيتزا:

enum class PizzaSize {

    SMALL, MEDIUM, LARGE

}

تسمية الثوابت في enum:

وفقًا للطريقة المتّفق عليها في كوتلن  Kotlin Coding Convention، يتم كتابة الثوابت Constants في كوتلن، باستخدام الحروف الإنجليزية الكبيرة Uppercase. أمّا إذا كان الثابت يتكون من كلمتين أو أكثر، يمكن كتابته إما بأحرف إنجليزية كبيرة مفصولة بشرطة سفلية (مثل RED_COLOR)، أو استخدام طريقة الجمل CamelCase التي تبدأ بحرف كبير (RedColor).

طباعة قيم الثوابت:

يمكننا طباعة قيم الثوابت في الصنف Rainbow، باستخدام اسم الصنف:

وستكون نتيجة الطباعة:

BLUE

هذه هي القيمة التي يحملها كائن الـ enum المسمى BLUE في هذه اللحظة. ولكن أليس هذا هو نفس إسم الكائن الذي وضعناه داخل الصنف Rainbow، هل هذا هو كل ما يمكن فعله مع الصنف enum؟ بالطبع لا. أصناف enum لديها الكثير من الخصائص والمزايا والتي سنمر على العديد منها بالتدريج في الفقرات أدناه.

إضافة خاصية لباني الصنف:

لأن enum عبارة عن صنف، لذلك يحتوي على باني Constructor. بالتالي، يمكننا استخدام هذا الباني، لتمرير بيانات الكائنات داخل الصنف:

وضعنا خاصية أسميناها color من نوع البيانات String، داخل أقواس باني الصنف Rainbow. وفي هذه الحالة، سيكون من الإجباري تمرير قيمة هذه الخاصية في الثوابت داخل الصنف، كما يظهر في الشفرة أعلاه.

الآن عند استدعاء الكائن BLUE، يمكننا استدعاء قيمة الخاصية الخاصة به:

وستكون نتيجة الطباعة هذه المرة، هو النص الذي وضعناه بين قوسي الكائن BLUE:

Blue

إضافة خاصية أخرى للكائنات:

يتم تمثيل الألوان في صفحات الويب، عبر استخدام الأرقام الست عشرية. فمثلَا، اللون الأحمر يتم تمثيله بهذه الطريقة:

 #FF0000

 حيث FF هي قيمة اللون الأحمر Red، وهي درجة التّشبّع من اللون. أمّا 00 في الوسط هي قيمة اللون الأخضر Green، وهي تعبّر عن غياب هذا اللون. والـ 00 الأخيرة هي قيمة اللون الأزرق Blue. ولغياب اللونين الأخضر والأزرق في هذا الرقم، يكون هذا الرقم الذي يمثل اللون الأحمر فقط.

بالتالي، تكون الأرقام التي تمثّل اللون الأخضر: 00FF00، واللون الأزرق: 0000FF. باستخدام هذه الطريقة، وهذه الألوان الثلاث، يتم تمثيل كل الألوان في صفحات الويب. ويعرف هذا النمط بـ RGB، كاختصار لـ Red, Green, Blue. 

نحن الآن سنستفيد من هذا الأمر ونضيف خاصية rgb لصنف Rainbow:

أصبح لدينا خاصيتين لكل كائن في صنف Rainbow. يمكننا طباعة أيّاً منهما أو كليهما:

وستكون نتيجة الطباعة:

إضافة دوال للـ enum:

كما قلنا سابقًا، الـ enum هو عبارة عن صنف عادي. لذا يمكننا إضافة الدوال إليه أيضًا. وسنستفيد من هذه الإمكانية، في تعديل شفرتنا أعلاه لتصبح أكثر وضوحًا، بإضافة دالة تعيد معلومات كل لون عند استدعائها:

fun getColorInfo() = “The $color represented with $rgb in web colors”

هذه المرة، يمكننا استدعاء هذه الدّالة عبر استخدام صنف الـ enum، والكائن المراد الحصول على معلوماته:

وعند تنفيذ الشفرة، سيكون الناتج:

عند إضافة دالة أو أي شفرة تحت الكائنات في صنف enum، نضع فاصلة منقوطة ( ; ) بعد آخر كائن. نلاحظ أيضًا في الصورة، أنّنا استخدمنا مفهوم التغليف Encapsulation بوضعنا كلمة private قبل كل خاصية في الباني. لأنه أصبح لدينا دالة تعيد معلومات كل الكائنات في الصنف، لذلك لم يعد هناك داعي للوصول واستخدام الخاصيتين خارج الصنف.

الخاصيّات والدوال المرفقة بالـ enum:

الآن نعرف كيفية إنشاء صنف enum، وإضافة خاصيّات ودوال إليه. ولكن في حالات عملية كثيرة، لا يعد هذا كافيًا. إذ سيتوجب علينا استخدام الخاصيّات والدوال التي تأتي مع الـ enum في مكتبة كوتلن القياسية. كمثال لدينا: 

  • خاصية name، والتي تعيد لنا اسم الكائن الثابت في صنف enum.
  • وخاصية ordinal، والتي تعيد لنا رقم موقع الكائن في الصنف. مع العلم أن أول كائن، يكون رقم موقعه صفر، والثاني 1، والثالث 2، وهكذا. بطريقة مشابهة للفهرس في تجميعة القوائم والمصفوفات.
  • دالة ()values، والتي تعيد لنا مصفوفة تحوي كل كائنات الـ enum.
  • ودالة ()valueOf، والتي تعيد لنا اسم الكائن الثابت في صنف enum، بإرسال قيمة اسمه كنص من النوع String إليها. مع العلم أن الدّالة حساسة لحالة الأحرف، فـ “RED” لا تساوي “Red”.

مثال على الخاصيّات والدوال:

لفهم عمل الخاصيّات والدوال من الفقرة السابقة، دعونا نكتبها كلها في شفرة واحدة:

لدينا في الشفرة أعلاه، الدّالة الرئيسية ()main، وصنف enum، ودالة ()isRainbowContains والتي سنستخدمها لمعرفة ما إذا كان لون معين متواجد في صنف الـ enum. سنشرح الشفرة بالتفصيل في الصورة التالية:

  1. استخدمنا الخاصيّة name، للحصول على اسم كائن enum، واسندنا النتيجة إلى المتغير colorName1. ولأن الخاصيّة name تعيد نتيجة من النوع String، سيكون نوع بيانات المتغير من النوع String. وهذا يعني، أنه يمكننا استخدام كل الدوال والخاصيات من المكتبة القياسية، والتي يمكننا استخدامها مع النوع String، مع هذا المتغير.
  2. عند استخدام دالة ()valueOf، يجب أن نرسل لها اسم الكائن المراد كما هو. فـ “Green” تختلف عن “GREEN” لإختلاف حالة الأحرف. هذه المرة سيتم إسناد كائن enum إلى المتغير colorName2. وسيكون نوع بيانات المتغير، هو صنف enum الذي يحوي الكائن. بالتالي، يمكننا استخدام كل الدوال والخاصيات من المكتبة القياسية، والتي يمكننا استخدامها مع النوع enum، مع هذا المتغير.

لن يكون هناك فرق عند طباعة المتغيرين colorName1 و colorName2، فالإثنين يطبعان اسم الكائن عند وضعهما في دالة الطباعة. ولكن، سيكون الفرق هو استخدام الخاصيّات والدوال المختلفة معهما. لأن الأول كائن من النوع String، والثاني كائن من النوع enum الخاص بنا، وهو في حالتنا هذه Rainbow.

  1. عند استخدام خاصيّة ordinal مع كائن enum، ستعيد لنا رقم الموقع الذي يتواجد به الكائن داخل الصنف enum. وتبدأ هذه الأرقام، من الرقم صفر للكائن الأول. وبهذه الطريقة نجد أن الكائن GREEN في الموقع رقم 3، وهو ما تم طباعته في الصورة.
  2. في هذا السطر، أرسلنا القيمة “black” إلى دالة ()isRainbowContains والتي ستعيد لنا نتيجة true أو false. النتيجة العائدة هي ما ستطبعها دالة الطباعة ()println.
  3. دالة ()isRainbowContains دالة خاصة بنا أنشأناها لتبحث لنا ما إذا كانت القيمة النصية التي نمررها لها متواجدة في صنف Rainbow. تستقبل الدالة القيمة المرسلة إليها في معامل color. ثم تدور على كل كائنات الـ Rainbow، عبر استخدام حلقة التكرار for.

فكما نعرف من الدروس السابقة، أنه يمكننا الدوران على التجميعات والنطاقات والمصفوفات، عبر استخدام الحلقة for. إذًا للدوران على كائنات في صنف enum، نحتاج أن تأتي في إحدى هذه الأشكال. وهذا تمامًا هو عمل دالة ()values، والتي تُجمِّع كائنات ال enum، في مصفوفة واحدة، يصبح من السهل التعامل معها عبر حلقة التكرار for.

المتغير المؤقت الذي اخترنا له اسم enum في الحلقة، سيُمثِّل كائن واحد في كل دورة للحلقة. لذا في كتلة if الشرطية، نتحقق ما إذا كان اسم الكائن الحالي، والذي نصل إليه باستخدام الخاصيّة name، يتطابق مع قيمة المعامل color. 

لتجنب الأخطاء، نحتاج أيضًا إلى تحويل قيمة المعامل color إلى حروف كبيرة باستخدام دالة ()uppercase من المكتبة القياسية، لتصبح “BLACK” بدلًا عن “black” بعد التحويل. وهذا لأن أسماء الكائنات تكون بالحروف الكبيرة، فإذا لم نفعل ذلك، سيتم إعتبار أن “black” غير موجودة في صنف Rainbow.

كانت نتيجة طباعة القيمة العائدة من الدالة false، لأن “BLACK” لا يتواجد ضمن مجموعة كائنات صنف Rainbow.

  1.  صنف Rainbow من النوع enum، وبه مجموعة كائنات تُمثِّل ألوان قوس قزح.
  2. النتيجة النهائية لتنفيذ الشفرة في تبويب Run في برنامج IntelliJ.

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

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