تعلّم البرمجة بلغة كوتلن (40): أنواع البيانات التي تقبل قيمة فارغة Nullable Types.

تعلّم البرمجة بلغة كوتلن (40): أنواع البيانات التي تقبل قيمة فارغة Nullable Types.
أستمع الى المقال

شرحنا في درس المتغيرات والقيم، كيف يمكننا إسناد قيمة من إحدى أنواع البيانات الأساسية في كوتلن، إلى متغير. ويعتبر هذا المتغير، مؤشراً يؤشِّر إلى هذه القيمة في الذاكرة. وعند استخدام هذا المتغير لاحقًا في شفرتنا، فنحن فعليًا نتعامل مع القيمة المسندة إليه.

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

ولكن ماذا إذا احتجنا فعليًا في برنامجنا، إلى إسناد قيمة للمتغير لاحقًا، وليس في وقت الإعلان عنه؟ في هذه الحالة، نسند إلى هذا المتغير قيمة لا شيء null. وسيكون نوع بيانات هذا المتغير، هو نوع يقبل القيمة الفارغة، وليس من أنواع البيانات الأساسية العادية. وهو ما سنوضحه تفصيليًا في الفقرات أدناه.

ما هو null:

عند إسناد القيمة null إلى المتغير، يصبح المتغير عبارة عن مؤشر يؤشِّر إلى لا قيمة (أي فراغ) أو غياب تام ﻷية قيمة. في لغات مثل جافا، يمكننا الإعلان عن متغير من النوع String مثلًا، دون إسناد أية قيمة له. وسيتم إسناد القيمة null، ضمنيًا.

هذا الأمر أدى إلى كثير من المشاكل. لأن استخدام هذا المتغير، ونسيان إسناد قيمة له لاحقًا، سيؤدي إلى حدوث إستثناء (NullPointerException (NPE وتحطّم البرنامج بالكامل. هذا الاستثناء، هو العدو الأول للمبرمجين، وخاصة مبرمجي جافا. لتسببه في أخطاء قد تصعب كثيرًا معالجتها، وتكون مكلفة في كثير من الأحيان. 

لتجنب ذلك، منعت كوتلن الإعلان عن متغير دون إسناد قيمة له حتى ولو كانت الكلمة null. بهذه الطريقة ستكون أغلب المتغيرات في شفرتنا، تحمل قيمة ما. وسيكون من الواضح ما هو المتغير الذي ليس له قيمة وقد يسبب مشاكل لاحقَا. وعند طباعة المتغير بدون إسناد قيمة، سيتم طباعة الكلمة null، دون التسبب في تحطّم البرنامج بالكامل.

للتحكم في هذا الخطأ، وضعت كوتلن نظام للأنواع، يكون به أنواع بيانات تقبل القيم الفارغة وأخرى لاتقبل. لذا، حينما نعلن عن متغير بـ var أو val، نحن افتراضيًا نستخدم الأنواع التي لا تقبل القيم الفارغة. أمّا إذا كنا فعليًا نحتاج إلى أن يكون المتغير بدون قيمة، يمكننا عندها استخدام النوع الثاني.

التسلسل الهرمي للأنواع والأصناف في كوتلن:

كل الكائنات من أنواع البيانات الأساسية وحتى الأصناف التي ننشئها في برنامجنا، يتم تمثيلها عبر العلاقة نوع فرعي Subtype ونوع فوقي أو أعلى Supertype. الأب لكل هذه الأنواع والأصناف والذي يكون في أعلى الهرم، هو النوع Any، والنوع Nothing في أسفله:

الصورة للتوضيح لذا تحوي بعض الأنواع في كوتلن وليس كلها.

كما يظهر في الصورة، أن نظام الأنواع يتكون من نوعين. النوع الأول هو النوع الافتراضي الذي يتم وضعه للمتغيرات عند الإعلان عنها. أمّا عند الحاجة إلى نوع ذا قيمة null، فيجب أن نضع له النوع صراحةً باستخدام علامة الاستفهام، وإلا سيكون نوعه هو ?Nothing.

الإعلان عن متغير يؤشِّر إلى قيمة فارغة null:

عندما نعلن عن متغير من نوع بيانات معيّن، تفترض كوتلن أن نوع بياناته هو من النوع الأول الافتراضي في نظام الأنواع:

val hello = “Hello World”

أو يمكننا وضع النوع صراحةً:

val hello: String = “Hello World”

في كلا الحالتين، سيكون نوع بيانات المتغير هو النوع String، لأنه يؤشِّر إلى نص (سلسلة من المَحارِف) في الذاكرة. 

أو يمكننا جعل المتغير بدون قيمة، بإسناد null إليه، ثم إسناد القيمة النصية لاحقًا:

var hello = null

عندها سيكون نوع المتغير hello، هو النوع ?Nothing، وهو النوع أسفل كل الأنواع في كوتلن. لذلك، عندما نحاول لاحقًا تغيير قيمة المتغير إلى النوع String، بإسناد قيمة نصية إليه، لن يقبل المترجم عملية الإسناد هذه. وهذا ما يظهر عند وضع مؤشر الفأرة على الخطأ:

الخطأ يقول، أننا نحاول إسناد قيمة من النوع String، إلى متغير من النوع ?Nothing. لنستطيع إسناد القيمة النصية للمتغير hello، يجب أن نضع له النوع ?String صراحةً عند إعلانه:

أعلنا عن المتغير hello باستخدام var، ووضعنا له النوع الذي يقبل القيم الفارغة باستخدام علامة الاستفهام ? مع اسم النوع. لذا، استطعنا إسناد null إليه. لاحقًا، أسندنا له قيمة نصية. وعند الطباعة كان المتغير بالفعل يحمل قيمة والتي سيتم طباعتها:

Hello World

استخدام المتغير ذا القيمة الفارغة null:

المتغير hello نوعه نَصي يقبل القيم الفارغة ?String، بالتالي هو يختلف عن النوع String. ولكن بالرغم من ذلك، يمكننا أن نستخدم معه الخاصيّات والدوال، تمامًا مثل المتغير الذي يكون من النوع الذي لا يقبل القيمة الفارغة String. المشكلة ستكون إذا كان المتغير ما زال null عند استخدامها معه:

في الشفرة أعلاه، نحاول طباعة طول (عدد محارف) المتغير النَّصي hello. ولأن المتغير ليس به قيمة، لن يتم ترجمة الشفرة من الأساس. لتجنب الخطأ، يمكننا استخدام كتلة if، وفحص ما إذا كان المتغير ما زال يحمل القيمة null:

في هذه الحالة، لن يحدث خطأ وسيتم طباعة عدد محارف قيمة المتغير إذا لم تكن null، أو طباعة كلمة null إذا كانت قيمته null.

الاستدعاء الآمن .?:

يمكننا دائمًا استخدام كتلة if، للتأكد من متغيرًا ما، ما إذا كان يحمل القيمة null قبل استخدامه. ولكن إذا كان لدينا عدد كبير من المتغيرات، ستصبح الشفرة كبيرة وتداخل كتل if، سيجعل منها صعبة القراءة. كالعادة، لدى كوتلن طريقة لتجنب تكرار الشفرة وتشعبها، هذه المرة بتوفير طريقة الاستدعاء الآمن Safe Call.

يتكون الاستدعاء الآمن من رمزين: علامة الاستفهام ( ? ) و النقطة ( . ) يمين علامة الاستفهام. ونضع هذين الرمزين، بعد كل متغير نتوقع منه أن تكون قيمته null:

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

جعل الأصناف الخاصة بنا تقبل القيم الفارغة null:

عندما ننشئ صنف خاص بنا في شفرة برنامجنا، تضع له كوتلن تلقائيًا، نوع يقبل القيم الفارغة وآخر لا يقبل:

كما نرى في الشفرة أعلاه، يمكننا إنشاء كائنات تحمل القيمة null، من كل الأصناف التي درسناها حتى الآن في هذه الدورة. وهذا يعني، أن أصنافنا أيضًا لديها نوعين مختلفين. كل نوع منها، يتم وضعه في مكانه المناسب في نظام الانواع في كوتلن. وترث تلقائيًا من الصنف الأعلى (الأب) الصنف Any أو ?Any.

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

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

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