کاتلین یاد بگیریم! (قسمت ۴)

Null Safety
Null Safety

سلام و امیدوارم هفته خوبی را سپری کرده باشین. هفته پیش در مورد انواع پایه گفتگو کردیم و طبیعتا اکیداً توصیه میکنیم که بخونید بخش قبل را و بعد بیاید سراغ این قسمتمون.

خوب قبل از اینکه شروع بکنم لازمه یک نکته‌ای را بگم. من هفته پیش فراموش کردم بگم این هفته که تعطیلات هست نمیتونم بنویسم و طبیعتا وقفه بیشتری خواهیم داشت. ازاینکه این موضوع را فراموش کردم بگم عذرخواهی میکنم :)

و بریم سراغ یکی ازجذاب‌ترین موضوعات برنامه‌نویسی یعنی:

خطای اشاره‌گر تهی یا NullPointerException

قبل از اینکه بریم سراغ این خطا و توانمندی کاتلین در جلوگیری از بروز آن به نظر بهتره یک صحبتی در مورد خود مفهوم null بکنیم. برای این مفهوم معانی زیادی هست مثل «تهی»، «بی‌مقدار» و خیلی تعابیر و معانی دیگه. اما اون معنی که من خیلی دوستش دارم را اولین بار در کتاب «مفاهیم بنیادی پایگاه داده‌ها» تالیف سید محمد تقی روحانی رانکوهی دیدم. در تعریف این واژه آمده:

«هیچ‌مقدار» یعنی مقدار ناموجود، مقدار ناشناخته، مقدار غیر قابل اعمال، مقدار تعریف نشده. گذشته از جزییات می‌توان گفت معادل است با همان مفهوم «داده نهست» و یا به تسامح « اطلاع نهست». *

خوب حالا که یک تعریف کامل برای این مفهوم داریم، میتونیم بریم سراغ اصل ماجرا که قدرت کاتلین در جلوگیری از بروز این خطا هست.

اگر قبلا برنامه‌نویسی کرده باشین به احتمال بسیار زیادی به این خطا برخورد کردید. این خطا به این موضوع اشار داره که شما قصد داشتید با یک شئ هیچ‌مقدار یک فراخوانی انجام بدید - مثلا یک تابع از اون شئ را صدا کنید. این خطا بسیار رایج هست و لازمه جلوگیری ازش اینه که مطمئن بشید شئ‌های برنامه شما همیشه مقدار دارند. اما در کاتلین کار ساده شده و عملا کامپایلر کاتلین به شما اجازه استفاده از یک متغیر null را نمی‌دهد**:

https://gist.github.com/sobhanattar/623b26d18514840d2ab6e457453fe6fa

به عبارت دیگر درزبان کاتلین امکان داشتن متغیری با مقدار نهست وجود ندارد مگر آن‌ که به‌صورت صریح در کد مشخص کنید که این متغیر قابلیت گرفتن مقدار null دارد:

https://gist.github.com/sobhanattar/206c958e4eee31d072fa1158618e45ff

توجه کنید که هرگاه ما ? را بعد از نام نوع - مثلا String - قرار دهیم به صورت واضح به کامپایلر می‌گوییم که مقدار این متغیر می‌تواند به یک شئ اشاره کرده و یا «نهست»، «هیچ‌مقدار»، null باشد. در مثال بالا ما این موضوع را برای نوع String دیدیم ولی به همین شکل برای سایر نوع‌ها نیز امکان‌پذیر است

https://gist.github.com/sobhanattar/60646e414f51a61d5e145df96be6ec2e

اپراتور فراخوانی امن:

خوب تا حالا راجع به اینکه چجوری مقدار null را داخل یک متغیر قرار بدیم صحبت کردیم ولی الان میخوایم بگیم که چجوری کاتلین از این خطای آزاردهنده جلوگیری میکنه. کد زیر را در نظر بگیرید:

https://gist.github.com/sobhanattar/e799f6eaf18f5a531857bfa11b2e16f2

کد بالا کامپایل نمیشه چون ما داریم طول یک متغیر از یک شئ null را درخواست میکنیم که خوب بی‌معنی هست و در زبان‌های دیگه به شما خطای NullPointerException را میدهد. اما اینجا کامپایلر کاتلین اجازه کامپایل این کد را نمی‌دهد و در نتیجه این کد به مرحله اجرا نخواهد رسید چون ممکن است که متغیر ما مقدار null داشته باشد. در واقع کاتلین اجازه کامپایل کدی را که احتمال بروز این خطا را بدهد نمی‌دهد.

برای رفع این مشکل کاتلین اپراتوری با نام اپراتور فراخونی امن .? معرفی کرده که در مثال زیر کاربرد اون را میبینیم:

https://gist.github.com/sobhanattar/f837f2e3e80806f4ca894858b42cfcf9

در مثال بالا با افزودن «اپراتور فراخوانی امن» یا همان .? به متغیر پیش از فراخوانی یک ویژگی از آن، ما به طور مشخص به کامپایلر اعلام می‌کنیم که تنها در صورتی ویژگی length متغیر را فراخوانی کند که متغیر نهست یا null نباشد. در صورتی که متغیر null باشد، کامپایلر کاتلین رشته "null" را به عنوان «نتیجه» عبارت v?.length در نظر گرفته و آن را چاپ می‌کند. این موضوع نه تنها برای ویژگی‌ها بلکه برای متدها و توابع شئ نیز صادق است.

توجه داشته باشید که زمانی که تابعی از یک متغیر «هیچ‌مقدارپذیر» را فراخوانی ‌می‌کنید، مقدار بازگشتی آن نیز «هیچ‌مقدار پذیر» یا nullable خواهد بود. بنابراین و برای مثال در کد زیر مقدار بازگشتی از عبارت v?.length زمانی که v نهست است، ?Int خواهد بود.

https://gist.github.com/sobhanattar/166fddcdb06710af170bce4b1703043e

برای رد کردن چک کردن هیچ‌مقدارپذیری می‌توانیم به جای .? از .!! استفاده کنیم. اگرچه این کار توصیه نمی‌شود زیرا منجر به بروز خطای NullPointerException خواهد شد.

https://gist.github.com/sobhanattar/fa9dac34f28844824d77520ecdba218b

اپراتور الویس: :?

این اپراتور که هم‌نام اون خواننده مشهور هم هست :) برای مقداردهی یک متغیر که مقدار null دارد مورد استفاده قرار می‌‌گیرد. مثال زیر را خیلی خوب این مطلب را نشون میده:

https://gist.github.com/sobhanattar/f934d8ebb90a45bd8e509e361a84dccd

در این مثال کامپایلر وقتی با null در متغیر username مواجه می‌شود به دلیل وجود اپراتور الویس مقدار "No Name" (مقدار جایگزین) را داخل متغیر name قرار می‌دهد. در صورتی که اگر مقدار username برابر با هیچ‌مقدار نبود، مقدار متغیر name با متغیر username یکسان می‌بود.

https://gist.github.com/sobhanattar/b6b7a355ddc986944b61200ed8c5eacf

این هم از بحث nullable و خطای NullPointerException که بسیار مسئله مهمی در برنامه نویسی هست. در قسمت بعدی میریم سراغ حلقه‌ها و کنترل گردش :)

* اکیدا توصیه میکنم مطالعه این کتاب را برای درک صحیح مفاهیم پایگاه داده‌ای رابطه‌ای.
** به پیشنهاد سجاد :) کدها را داخل gist بردم که استفاده کردن ازشون ساده تر باشه ولی خوب دق میده تا لود کنه!!