کاتلین از ابتدا : extension functions ( قسمت اول )

۱− مقدمه

کاتلین قابلیت جدیدی به اسم extensio functions رو معرفی کرد ، که تعریف کتابی و کلیش اینجوریه : روشی سودمند برای extend کردن از کلاس‌های موجود، به طوری که نه از وراثت ( inheritence ) استفاده شده باشد و نه از هیچ گونه Decorator patternای

بدون استفاده از هیچ API خاصی و فقط با استفاده از کاتلین خالی میتونیم extension functionها رو بسازیم.

این ویژگی خیلی مفید، باعث میشه تا هم کد ما خواناتر بشه و هم اینکه از لحاظ maintainability بهتر بشه. دلیل این حرفی که زدم اینه که، ما میتونیم متدهایی رو بسازیم که هر کدوم یک کاربردی داشته باشن، با وجود اینکه - همونطور که گفتم - قسمتی از اصل کد هستن ( نه کتابخونه یا همچین چیزی ).

یه تعریف ساده‌ی دیگه‌ای هم برای extension function ها وجود داره، و اون اینه که : به تابعی اطلاق میشه که member یا عضو یک کلاس هست ولی داخل اون کلاس تعریف نشده، بلکه خارج از اون کلاس تعریف شده و قرار داره. برای روشن تر شدن این موضوع یک مثال ساده پایین زده شده که آخرین کاراکتر یک رشته رو پیدا میکنه.

 package strings
fun String.lastChar(): Char = this.get(this.length - 1)

همه‌ی کاری که لازمه تا انجام بشه قرار دادن اسم کلاس یا اینترفیس پیش از اون تابعی که دارین میسازین هست. به اسم اون کلاسه گفته میشه receiver type و به اون متغییری که دارین فراخوانیش میکنید، receiver object گفته میشه. به شکل پایین توجه کنید:

برای فراخوانی و استفاده از تابع میتونید از سینتکسی شبیه به سینتکس class member ها استفاده کنید، مثلا :

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

توی این مثالی که نوشته شد، receiver type ، استرینگ هست و "Kotlin" هم receiver object هست.

به قولی، شما متد مورد نظر خودتون رو به کلاس String اضافه کردین. درسته که کلاس String بخشی از کد شما نیست، و حتی سورس کدش رو استفاده نکردید، ولی با این روش یعنی extension function، میتونید
به روش متفاوتی توابعی رو که نیاز دارین از اون کلاس extened کنید. حتی این موضوع اهمیتی نداره که این String ای که میخواید ازش استفاده کنید به زبان کاتلین هستش، یابه زبان جاوا یا هر زبان JVMای دیگه‌ای.

داخل تابع، میتونید با استفاده از this متغییری رو که به extension function اضافه کردین ، مورد استفاده قرار بدین. حتی میتونید همین this رو حذف کنید و بدون this از اون متغییر استفاده کنید.

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

توضیح کد بالا :‌ همونطور که میبینید Receiver object ها بدون this هم میتونن مورد استفاده قرار بگیرن.

شما توی extension function میتونید به طور مستقیم و بی واسطه به propertyها و متدهای کلاسی که دارین ازش extened میکنید دسترسی پیدا کنید، انگار که این extension function رو داخل همون کلاس تعریف کردین. ولی یه نکته‌ی مهمی وجود داره و اون اینه که درسته که این تابع سازوکارش شبیه به متدهای داخل اون کلاس میشه، ولی با این وجود شما نمیتونید به توابع و متغییرهای private و protectedای که داخل این کلاسه تعریف شدن دسترسی پیدا کنید.

مساله‌ی import کردن و extension functions

وقتی که یک extension function رو تعریف میکنید، خود‌به‌خود توی کل پروژه قابل دسترس نمیشه. بلکه باید مثل هر متد یا کلاسی ، import بشه. این به ما کمک میکنه که یه وقت توی نام گذاری به conflictای برنخوریم. extension function ها رو به صورت زیر import میکنیم :

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

البته اینجوریم میشه :

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

اینجوریم میشه اسمشو موقع import کردن عوض کرد :

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

فراخوانی کردن extension functionها از جاوا

در حقیقت یک extension function، یک متد استاتیک هست که هر receiver object رو به عنوان اولین آرگومانش دریافت میکنه. برای فراخوانی کردن اون نیازی نیست که خودتون رو درگیر ساخت آبجکت‌های adapter کنید و هیچ runtime overhead ( سربار زمان اجرا )ای نخواهید داشت.

همین مساله باعث شده تا فراخوانی کردن extension functions از جاوا خیلی راحت و سرراست باشه: برای این کار کافیه اون extension function رو به صورت یک تابع top-level بنویسید یعنی اینکه اون رو خارج از یک کلاس و داخل یک پکیج تعریف کنید تا به یک تابع استاتیک کامپایل بشه، خب مثلا فرض کنید که یک extension function رو توی کاتلین به صورت زیر تعریف کردین :

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

خب فرض کنید اسم پکیجی که تابع بالا توش قرار گرفته Example.kt باشه.

حالا برای اینکه بخوایم همون extension function رو توی جاوا استفاده کنیم، بایستی به صورت زیر ازش استفاده کنیم :

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

نکته‌ای که وجود داره اینه که اول اسم پکیج رو مینویسیم بعدش کلید واژه‌ی Kt رو میگذاریم، و بعد نقطه نام extension function رو میاریم. و اینکه این تابع در جاوا دو پارامتر داره که اولیش همون متغییری هست که extension function رو بهش اضافه کردیم و دومی پارامترهای خود extension function هست.

...