davud hosseiny
davud hosseiny
خواندن ۳ دقیقه·۷ سال پیش

چطور از دست "!!" خلاص شیم

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

mutable : تغییر پذیر

immutable : تغییر ناپذیر



ا NullSafety یکی از بهترین ویژگی های کاتلین هست. این ویژگی باعـث میشه که شما در سطح زبان برنامه نویسی به تهی پذیری (nullability) فکر کنید در نتیجه میتونید از بسیاری NullPointerException های مخفی که در جاوا معموله، خلاص بشید. وقتی از ابزارهای اتوماتیک برای تبدیل کد جاوا به کاتلین استفاده میکنید کلی !!(بخوانید دو علامت تعجب) میبینید. تا حد ممکن باید تعداد اونها رو کم کرد دلیلش اینه که !! به این معنی هست که "در این نقطه پتانسیل وجود KotlinNullPointerException هندل نشده وجود داره".

کاتلین مکانیزم های هوشمندانه ای برای رفع این مشکل داره، شاید با یکی یا چندتا از اونها آشنا باشید. ما اینجا 6 راه برای حل این مشکل معرفی میکنیم.


1- استفاده از val به جای var

کاتلین باعث میشه شما در سطح زبان به immutability فکر کنید و این خیلی عالیه. val فقط خواندنی و var (متغیر variable) قابل تغییر هست. توصیه میشه تا حد ممکن متغیرهاتون رو فقط خواندنی تعریف کنید. چون Thread safe هست و با برنامه نویسی تابعی هماهنگه. اگه متغیرهای val رو به صورت immutable به کار ببریم لازم نیست دیگه نگران تهی پذیری باشیم. در نظر داشته باشید val میتونه mutable هم باشه(اگر برای متغیرتون getter ننوشتید متغیرتون immutable هست و لازم نیست نگران چیزی باشید).


2- استفاده از lateinit

گاهی شما نمی‌تونید از متغیرهای immutable استفاده کنید. برای مثال در اندروید متغیری رو در نظر بگیرید که در onCreate مقداردهی میشه. برای این مورد kotlin کلمه کلیدی lateinit رو داره.

شما میتونید این کد رو:

https://gist.github.com/77c47875f9f758f872e13dd2987bfda7.git

به این تغییر بدید

https://gist.github.com/4b60a9be3917011db7d8f55ec7ca72d0.git

در نظر داشته باشید اگه متغیر lateinit رو قبل از مقدار دهی استفاده کنید باعث خطا از نوع UninitializedPropertyAccessException میشه.

متاسفانه نمی​‌​​​​​توان متغیر lateinit از انواع داده primitive(مثل Int) تعریف کرد. برای این نوع داده‌ها میتونید از Delegates استفاده کنید:

private var mNumber: Int by Delegates.notNull<Int>()


3. استفاده از lazy

با استفاده از by lazy می​​​‌تونید متغیرهای immutable و تهی ناپذیر تعریف کنید، که در زمان مناسب مقدار دهی بشوند.

مثال:

https://gist.github.com/78e00bd6dac15ea8d577e8b79bcf8b09.git

ا messageView فقط در اولین استفاده، با اجرای بلاک کد lazy مقداردهی میشه و در استفاده‌های بعدی همون مقدار مورد استفاده قرار میگیره.

این طوری ما می‌تونیم حتی متغیرهایی که در لحظه ایجاد شدن شی آماده مقداردهی نیست‌اند(مثل ویوها در اندروید) رو هم immutable تعریف کنیم.


4. استفاده از let

کد زیر نمونه معمولی از ارورهای زمان کامپایل تو کاتلین هست:

اینجا Smart cast ممکن نیست.
اینجا Smart cast ممکن نیست.

شاید همیشه براتون سوال شده باشه که چرا کامپایلر کاتلین اینجا نمیتونه Smart cast رو انجام بده. دلیلش اینه که ممکنه thread دیگه ای غیر از threadی که null بودن رو داره چک میکنه این متغیر رو null کنه.

این مشکل آزار دهنده است. من این رو مطمئن ام که این متغیر نمیتونه بعد از چک برای null نبودن تغییر کرده باشه. خیلی از برنامه نویس‌ها این مشکل رو با !! سریع حل میکنن:

https://gist.github.com/dd5ad562bf28c9ebaee5de088b4dc3f6.git

اما راه حل بهتر برای این مشکل استفاده از متد let هست:

https://gist.github.com/f7f4bf39089f0cfdfff470c9c580d718.git

اگه mPhotoUrl نال باشه کد داخل let اجرا نمیشه.

5. استفاده از Elvis operator

اگر ممکنه متغیر null باشه و ی مقدار پیش فرض جایگزین برای موقع null بودن در نظر گرفته اید Elvis operator خیلی به درد می‌خوره. ما میتونید این کد رو:

https://gist.github.com/bc57427e3a931bcb376619143742628e.git

به این تغییر بدیم:

https://gist.github.com/731df413c7a04decc44c25ec92ff925f.git

نتیجه اش اینه میشه که علاوه بر حل مشکل !! کد کمتر و تمیزتری داریم.(کد کمتر اکثرا با کد بهتر مترادفه :) )


6. زیر پا گذاشتن مقررات

باز هم مواردی وجود داره که شما مطمئنید که متغیر با اینکه تهی پذیر هست نمی‌تونه null باشه و اگر null باشه ی باگی تو کدتون وجود داره که باید اصلاح بشه. با این حال !! به شما چیزی جز ی Exception بدون پیام که دیباگ کردنش مشکله نمیده. پس بهتره از متدهایی مثل requireNotNull یا checkNotNull به همراه پیغام خطای مناسب استفاده کنید که دیباگ کردن رو هم راحتتر میکنه.

تغییر این کد:

https://gist.github.com/b768016f18024427b3c788988f7e1dbb.git

به این کد:

اhttps://gist.github.com/35ba25bc013e7c5738f9f14d58e30993.git


نتیجه گیری

اگر از 6 تکنیک بالا استفاده کنید می‌توانید همه !!ها رو از کدتون حذف کنید. وقتی با این کار کد شما مطمئن‌تر، تمیزتر و دیباگ آن راحتتر خواهد بود. اگر راههای دیگه ای برای حذف !! میشناسید یا مشکلی در این مطلب هست تو کامنت ها بهم اطلاع بدید.

kotlinlateinitlazyکاتلینnullpointerexception
شاید از این پست‌ها خوشتان بیاید