4- کاتلین از ابتدا : پکیج‌ها و توابع پایه

توی قسمت قبلی مطالبی رو در مورد collectionها و rangeها یاد گرفتیم. توی این قسمت، ما آموزش رو با نگاهی به پکیج‌ها و معرفی توابع ادامه میدیم.

1- پکیج‌ها ( Packages )

اگه با جاوا آشنایی داشته باشید، حتما می‌دونید که جاوا از پکیج‌ها برای دسته‌بندی کلاس‌ها استفاده می‌کنه، برای مثال، پکیج java.util تعدادی کلاس utility مفیدی داره. پکیج‌ها با کلمه‌ی کلیدی package تعریف میشن، و هر فایلی که با کلیدواژه‌ی package شروع میشه، داخلش کلاس‌ها، توابع یا اینترفیس‌هایی تعریف شدن.

تعریف

اگه به کد پایینی نگاه کنید می‌بینید که ما یک پکیج به اسم com.chikekotlin.projectx رو با استفاده از کلیدواژه‌ی package تعریف کردیم. در ضمن یک کلاس رو هم به اسم MyClass ( که توی قسمت‌های آینده کلا بیشتر در مورد کلاس‌ها صحبت می‌کنیم ) رو هم داخل این پکیج تعریف کردیم.

https://gist.github.com/sajjadyousefnia/71492f25869ffad756ef8ee0b70bbe3e

اسم کامل کلاس MyClass هست : com.chikekotlin.projectx.MyClass

https://gist.github.com/sajjadyousefnia/19259170fd62ce88f738860debf4468a

توی کد بالایی، ما یک تابع Top-level رو ایجاد کردیم ( که به طور خلاصه‌وار بعدا معرفیش می‌کنیم ). شبیه کلاس قبلی که گفتیم، اسم کامل انتخاب شده برای کلاس ()saySomething هست : com.chikekotlin.projectx.saySomething

وارد کردن ( import )

توی کاتلین، ما از کلیدواژه‌ی import برای مشخص کردن مکان کلاس‌ها، توابع، اینترفیس‌ها یا آبجکت‌هایی که میخوان import بشن استفاده میشه. از طرف دیگه، ما نمی‌تونیم توابع یا اینترفیس‌ها رو به طور مستقیم import کنیم - این قضیه فقط در مورد کلاس‌ها و اینترفیس‌ها هست. ما از import برای دسترسی یه یک تابع، اینترفیس، کلاس یا آبجکتی که بیرون از پکیجی که ما تعریف کردیم قرار داره، استفاده می‌کنیم.توی قطعه کد بالا، ما تابع ()saySomething رو از یک پکیج دیگه import کردیم، و بعدش تابع رو اجرا کردیم.درضمن کاتلین از import کردن به شیوه‌ی Wildcard پشتیبانی می‌کنه، که این کار با استفاده از عملگر * انجام میشه. با انجام این کار می‌تونیم همه‌ی کلاس‌ها، اینترفیس‌ها و توابع تعریف شده داخل پکیج رو import کنیم. هرچند گفته میشه که بهتره این کارو انجام ندید.

https://gist.github.com/sajjadyousefnia/664edc0a009c5d387f2a901ca4a2548e

ه Import Aliasing

وقتی شما کتابخونه‌هایی دارین که اسمشون با اسم توابع فعلی تناقض داره ( مثلا یه نمونه اینکه اسمشون یکی باشه )، می‌تونید از کلیدواژه‌ی as استفاده کنید تا از اون Entity با یک اسم موقت استفاده کنید.

https://gist.github.com/sajjadyousefnia/fe73708e5ce39f0980db752fbcb0a37a

یادتون باشه که از اسم موقتی فقط توی فایلی که شما گفتید استفاده میشه.

2- توابع

یک گروهی از توابع که با هم یک کاری رو انجام میدن. جزییات پیاده‌سازی تابع مقصد برای تابع اولی معلوم نیست.

توی کاتلین، توابع با کلیدواژه‌ی fun تعریف میشن، به مثال توجه کنید :

https://gist.github.com/sajjadyousefnia/c80facd0ceadfbe2d0a98234a6a81c74

توی کد بالایی، ما یک تابع ساده به اسم ()hello رو تعریف کردیم که یک پارامتر به اسم name و از نوع String رو داره. تعریف نوع برای تابع اینجوری انجام شده : name : type

https://gist.github.com/sajjadyousefnia/6d0f45a8d6207f5f48ac4aac49eb6b3d

تابع بالایی شبیه قبلی هست ولی دقت کنید که نوعی که این تابع return می‌کنه Unit هست. به این خاطر که میشه گفت تابع هیچ مقدار خاصی رو برای ما return نمی‌کنه و فقط یک پیامی رو چاپ می‌کنه نوعی که return میشه از جنس Unit هست. Unit توی کاتلین یک آبجکت هست ( که بعدا در موردش بیشتر صحبت می‌کنیم ) و مشابه نوع void توی جاوا و C هست.

https://gist.github.com/sajjadyousefnia/70f983a648c8d1cfa41ac5cf8921a357

البته اینو هم بدونید که اگه ما به طور واضح نگیم که نوع return از جنس Unit هست، خود کامپایلر جنسش رو متوجه میشه.

https://gist.github.com/sajjadyousefnia/0aa03f93d427f67bb5bbea3c77dd6f37

توابع single-Line ( تک خطی )

توابع single-Line یا one-Line یا تک خطی توابعی هستند که کل دستورات در یک خط خلاصه شده. توی این تابع، ما از گذاشتن { و } برای دستور خودداری می‌کنیم. به عبارت دیگه، function block نداریم.

https://gist.github.com/sajjadyousefnia/ed8e5f5a8ea968075a5d5ef0c050ed86

تابع بالایی رو میشه به صورت single line خلاصه کرد :

https://gist.github.com/sajjadyousefnia/7e6b9e77acffb26d1dc8931fb7e008b6

این حذف کردن‌ها برای خلاصه‌تر کردن کد هست.

ولی باز هم میشه نوع return رو به طور واضح اعلام کنیم.

https://gist.github.com/sajjadyousefnia/b4c95942a1d2fb242ac1ba2c81ef488d

توابع named ( نام‌گذاری شده )

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

توی مثال پایینی، ما یک تابعی رو ایجاد کردیم که اسم کامل یک نفر رو چاپ می‌کنه.

https://gist.github.com/sajjadyousefnia/84961e35abfd58da03119d4a40800a45

برای فراخوانی تابع بالایی فقط کافیه که فراخوانیش کنیم، بنابراین :

https://gist.github.com/sajjadyousefnia/d2f706d06ddababb3dfcb84f8d3fca6e

اگه به تابع بالایی نگاه کنیم، شاید کامل مشخص نباشه که آرگومان‌های تابع رو برابر با چه پارامترهایی قرار دادیم ( اگرچه بعضی از IDEها به ما توی این قضیه کمک می‌کنن ). کسایی که از تابع استفاده می‌کنن، به امضای تابع نگاه می‌کنن تا بفهمن چه پارامتری با چه آرگومانی ست شده.

https://gist.github.com/sajjadyousefnia/718116bd9b8001faf6d47065f3305599

توی بالا می‌بینید که ما اسم پارامترها رو قبل از هر آرگومان‌ ذکر کردیم. با انجام این کار احتمال به‌وجود اومدن خطا به حداقل میرسه، مخصوصا که ممکنه اشتباهی آرگومان‌ها رو جابجا بنویسیم.

همونطور که گفتم، میشه موقع فرخوانی ترتیب پارامتر‌های نامگذاری شده رو تغییر داد. برای مثال :

https://gist.github.com/sajjadyousefnia/608db57c840616e23986e0170b8c3eca

توی کد بالایی ما جای firstName و LastName رو عوض کردیم.

پارامترهای پیش‌فرض ( Default )

توی کاتلین، میشه به هر کدوم از پارامترهای تابع یک مقدار پیش‌فرض داد. این مقادیر پیش‌فرض در صورتی استفاده میشن که، موقع فراخوانی تابع هیچ مقداری به اون آرگومان اختصاص پیدا نکنه. برای انجام همین کار توی جاوا، باید متدهای مختلفی که overload شدن رو بسازیم.

اینجا توی متد ()circumference ما متد رو با اضافه کردن یک مقدار پیش‌فرض برای پارامتر pi - که یک ثابت توی پکیج java.lang.Math هست - تغییرش دادیم.

https://gist.github.com/sajjadyousefnia/5ea38b3a0f2babe0d293ab410318ab44

موقع فراخوانی این تابع، ما هم می‌تونیم از مقدار تقریبی فرستاده شده، و هم از مقدار پیش فرض pi استفاده کنیم.

https://gist.github.com/sajjadyousefnia/881cd3834fbe75a900a6f1f0ed2a2f96

به یک مثال دیگه هم توجه کنید :

https://gist.github.com/sajjadyousefnia/8edb9954243e2d5f02dc5367943a480f

توی کد پایینی، ما متد رو فراخوانی کردیم، ولی کامپایل نمیشه:

https://gist.github.com/sajjadyousefnia/7cae5bce27409d8c880000331993b543

توی تابع بالایی ما firstname و lastname رو به تابع می‌فرستیم و می‌خوایم که از مقدار پیش‌فرض برای middlename استفاده بشه. ولی کامپایل نمیشه، به این دلیل که کامپایلر براش این مسئله مبهم شده. برای کامپایلر مشخص نیست که کلمه‌ی "Mgbemna" مربوط به پارامتر lastname هست یا پارامتر firstname ؟

برای حل این مشکل، باید از نام‌گذاری کردن استفاده کنیم.

https://gist.github.com/sajjadyousefnia/2f6e163031c273e8a2f305e4c26fa94c

قابلیت تعامل ( interpoerability ) با جاوا

توی جاوا متدها از قابلیت پارامترهای پیش‌فرض پشتیبانی نمی‌کنن، پس موقع فراخوانی یک متد از جاوا لازمه که به طور واضح و صریح همه‌ی مقدارهای پارامترها رو اعلام کنید. با این وجود کاتلین یک قابلیتی رو به ما میده که بتونیم با استفاده از انوتیشن JvmOverloads@ فراخوانی راحت‌تری از طرف جاوا داشته باشیم. این انوتیشن به کامپایلر کاتلین دستور میده که توابع overload شده‌ی جاوایی رو برای ما بسازه.

توی مثال پایینی ما تابع ()calCirumference رو با JvmOverloads@ انوتیشن گذاری کردیم.

https://gist.github.com/sajjadyousefnia/45b582b7b80d18e0358f4641e4792ee5

توی کد پایینی رو که مشاهده می‌کنین تابع ()calCirumference رو با استفاده از JvmOverloads@ انوتیشن‌گذاری کردیم.

https://gist.github.com/sajjadyousefnia/90eaea73b821e5f0b8f4483357b122e5

کد پایینی با استفاده از کامپایلر کاتلین ایجاد شده به همین خاطر فراخواننده‌های ( Callerهای ) جاوایی می‌تونن انتخاب کنن که کدوم یکی رو فراخوانی کنن.

https://gist.github.com/sajjadyousefnia/c51ab09038b62d9c64389e0c1d324824

توی آخرین کد جاوایی که تعریف شده، پارامتر pi حذف شده. این معنیش اینه که متد از مقدار پیش‌فرض برای متغییر pi استفاده خواهد کرد.

آرگومان‌های نامحدود

توی جاوا، می‌تونیم یک متدی رو ایجاد کنیم که با استفاده از قرار دادن (...) بعد از نوع لیستی پارمترها، تعداد از پیش تعیین‌نشده‌ای از آرگومان‌ها رو دریافت کنه. این کار رو هم میشه با استفاده از گذاشتن vararg قبل از اسم پارامتر هم انجام داد.

https://gist.github.com/sajjadyousefnia/8edcfa05f223650a8d34779f97ad3bd2

این vararg باعث میشه که ما بتونیم یک لیستی از پارامترها رو که با استفاده از کاما از هم جدا شدن رو فراخوانی کنیم. در حقیقت، لیست آرگومانها در قالب یک آرایه قرار می‌گیرن.

معمولا وقتی که یک تابع چندین پارامتر داره، پارامتر vararg آخر از همه قرار می‌گیره. البته امکانش هست که چند تا پارامتر بعد از vararg قرار بگیره، ولی اینجوری مجبورین موقعی که تابع رو فراخوانی می‌کنید از پارامترهای نام‌گذاری شده استفاده کنید.

https://gist.github.com/sajjadyousefnia/486b44884384ae42f0ba6709d245f0ee

برای مثال، توی کد بالایی، پارامتری که با استفاده از vararg تعریف شده، آخر از همه پارامترها قرار گرفته( کاری که معمولا انجام میدیم). با این وجود اگه نخوایم آخر از همه قرارش بدیم، اونوقت چی؟ توی مثال پایینی، به عنوان دومین پارامتر قرار گرفته.

https://gist.github.com/sajjadyousefnia/64007cf8a114cefdc7e086225238b345

همونطور که توی کد بالایی مشاهده می‌کنید، ما برای حل این مشکل آرگومان‌ها رو نام‌گذاری کردیم.

عملگر ( spread ) پخش

فرض کنید که بخوایم یک آرایه‌ای از متغییرهای integer رو به تابع ()printNumbers بفرستیم. تابع انتظار داره که به صورت لیستی از پارامترها دربیاد و همونجوری آرایه نمونه. در صورتی که بخواید مستقیما آرایه رو به ()printNumbers بفرستید، خواهید دید که کد کامپایل نمیشه.

https://gist.github.com/sajjadyousefnia/5ebeb851fbf1ee8e47f735a92b3f22db

برای حل این مشکل، لازمه که عملگر spread که به شکل * هست، رو داخل لیست آرگومان‌ها و بعد از intsArray بیاریم. کد بالایی مشکل ما رو حل میکنه و مکانیزمش شبیه به اینه که ما درایه‌های آرایه رو با کاما از هم جدا کنیم و بعدش به تابع بفرستیم.

ا Return کردن چند مقدار

بعضی مواقع لازمه تا چندین مقدار رو از یک تابع return کنیم. یک روش خوب برای این کار، تبدیل کردن به نوع pair و بعدش return کردن اونه. دو مقداری که می‌خوایم بعدا ازشون استفاده کنیم رو داخل pair قرار میدیم. از هر نوع داده‌ای در این روش میشه استفاده کرد، در ضمن لازم نیست که نوع دو متغییر یکسان باشه.

https://gist.github.com/sajjadyousefnia/bfc9be261b4e4106f02b90fed810ce1b

توی تابع بالایی ما یک pair جدید رو با استفاده از متغییرهای userName و userState که به ترتیب به عنوان آرگومان اول و آرگومان دوم هستن ایجاد کردیم، و اونا رو return کردیم.

نکته‌ی دیگری که بایستی بهش دقت کنید اینه که ما از تابعی به نام ()require داخل تابع getUserNameAndState استفاده کردیم. این تابع کمکی که داخل کتابخونه‌ی استاندارد قرار داره برای ایجاد یک حالت شرطی برای فراخوان‌کننده ( یا Caller ) تابع به کار میره، یا به قولی یک IllegalArgumentException نیز ()throw میشه ( که در قسمت‌های بعدی در مورد require بیشتر بحث می‌کنیم ). آرگومان دومی تابع require در صورتی که یک Exception نقض بشه، یک عبارتی رو چاپ میکنه. برای مثال، اگه موقع فراخوانی تابع getUserNameAndState ، این تابع 1- رو به عنوان آرگومان return کنه، به تصویر پایینی می‌رسیم :

ر
ر

دریافت داده‌ها از Pair

https://gist.github.com/sajjadyousefnia/28f9c798aca0621be693649c99f4d515

توی کد بالایی، ما با استفاده از propertyهای first و second به مقادیر اول و دوم Pair دستیابی پیدا کردیم.

به هرحال، راه بهتری هم برای انجام این کار هست :

https://gist.github.com/sajjadyousefnia/df701b0a42b01f1d5e64565961fe4dbe

کاری که ما توی قسمت بالا انجام دادیم اینه که به طور مستقیم مقدار return شده از Pair رو به ترتیب با متغییرهای name و state مقداردهی کردیم. به این روش destructing declaration گفته میشه.

ا Return کردن سه متغییر و بیشتر از سه متغییر

حالا چی میشه اگه بخوایم یکباره سه تا متغییر رو return کنیم؟ کاتلین یک نوع مفید دیگه به نام Triple داره.

https://gist.github.com/sajjadyousefnia/523c1a1244ba13a31e0063fab18f6dae

خب حتما از این ویژگی که میشه همزمان سه مقدار رو return کرد شگفت زده شدین. حالا سوال پیش میاد که چجوری میشه بیشتر از سه متغییر رو return کرد. در مورد این موضوع توی قسمت data classها بیشتر در مورد بحث می‌کنیم.

نتیجه‌گیری

توی این قسمت در مورد package ها و توابع پایه در کاتلین یاد گرفتیم. توی قسمت بعدی از این سری بیشتر در مورد توابع کاتلینی یاد می‌گیریم.

لطفا در صورت تمایل اگه جایی اشتباه نوشتم بفرمایید اصلاح کنم.