آشنایی با مفهوم سازنده (Constructor) در کاتلین (Kotlin) — به زبان ساده
احتمالاً شما نیز مانند اغلب برنامهنویسان اندروید کار خود را با جاوا آغاز کرهاید و سپس به کاتلین مهاجرت کردهاید که مورد استقبال گوگل و جامعه توسعهدهندگان اندروید قرار گرفته است. یکی از اولین تفاوتهایی که هنگام این مهاجرت از جاوا به کاتلین با آن مواجه میشویم، این است که سازنده در کاتلین یا همان Constructor به طرز متفاوتی تعریف میشود.
در جاوا با سازندههایی مواجه هستیم که کاملاً تعریف شده هستند. اگر یک سازنده تعریف نشده باشد، یک سازنده پیشفرض بدون آرگومان به صورت خودکار ایجاد میشود. سازندهها در بدنه کلاس جاوا تعریف میشوند و از طریق همپوشانی لیست پارامترها میتوان هر سازنده را از سازندههای دیگر متمایز ساخت.
به طور عکس در کاتلین چیزی به نام سازنده اولیه داریم که به صورت اختیاری در امضای کلاس تعریف میشود. علاوه بر سازنده اولیه میتوان چند سازنده ثانویه نیز داشت.
در ادامه مثالی از یک سازنده اولیه در کاتلین را مشاهده میکنید:
1class Dog constructor(val name: String) {}
در اینجا چند مسئله وجود دارند که باید به آنها توجه کنیم:
- استفاده از کلیدواژه constructor و شیوه نمایش آن در امضای کلاس پیش از تعریف بدنه کلاس. این کلیدواژه در این مثال اختیاری است و تنها زمانی الزام میشود که از annotation (مانند Inject@) استفاده کنید و یا مادیفایرهای پدیداری (مانند خصوصی ساختن سازنده) داشته باشید.
- سازنده اولیه نباید شامل هیچ کدی باشد. کد مقداردهی اولیه که به طور معمول در این سازنده ظاهر میشود، باید در یک یا چند بلوک مقداردهی اولیه (init) قرار گیرد.
با توجه به اطلاعاتی که از مثال فوق به دست آوردیم، اینک میتوانیم آن را با حذف کلیدواژه constructor بازنویسی کنیم و نتیجه یکسان خواهد بود:
1class Dog (val name: String)
نمونهای از کاربرد سازنده اصلی که در آن کلیدواژه constructor میتواند در عمل الزامی باشد، کلاسی است که نمیخواهید از خارج از کد کلاس وهلهسازی شود. برای نمونه شاید بخواهید همه وهلههای کلاس Dog از متد factory ایجاد شده باشند:
1class Dog private constructor(val name: String) {
2
3 companion object {
4 fun newDog(name: String) = Dog(name)
5 }
6}
تفاوت دیگر بین طرز کار سازنده در کاتلین در قیاس با جاوا در شیوه فراخوانی آنها است. به عبارت دیگر تفاوت در شیوه ساخت وهلههای جدید است. در کاتلین هیچ کلیدواژه new وجود ندارد. به جای آن وهلههای جدید از یک کلاس به صورت زیر ایجاد میشوند:
1val dog01 = Dog(name = "Sparky")
در کاتلین اگر صراحتاً یک سازنده اولیه تعریف نکنید، چنین سازندهای برای شما ایجاد میشود. برای نمونه کد زیر کاملاً قابل قبول است:
1class EmptyDog
وهلههای جدید class EmptyDog را میتوان با فراخوانی سازنده اصلی که بدون آرگومان ساخته شده به صورت زیر ایجاد کرد:
1val emptyDog = EmptyDog()
اگر میخواهید نوعی مقداردهی اولیه کد در زمان آغاز به کار کلاس اجرا شود، میتوانید این کار را در بلوکهای init دیگر انجام دهید:
1class Dog constructor(val name: String) {
2
3 init {
4 println("Registering $name with the AKC")
5 registerDogWithAKC()
6 }
7
8 private fun registerDogWithAKC() {
9 // TODO: perform registration tasks
10 }
11}
چند نکته در مورد بلوکهای مقداردهی
- بلوکهای مقداردهی با سازنده اولیه در ارتباط هستند.
- چه سازنده اولیه به صورت صریح تعریف شده باشد یا نباشد، هر بلوک مقداردهی زمانی که وهلهای از کلاس ساخته شود اجرا خواهد شد.
- اگر بیش از یک بلوک مقداردهی تعریف شده باشد، در این صورت به ترتیبی که در بدنه کلاس نوشته شدهاند، اجرا خواهند شد.
- فیلدهایی که در سازنده اولیه ظاهر میشوند، میتوانند از بلوکهای مقداردهی ارجاع دهی شوند. در مثال فوق میتوانید ببینید که فیلد name در بلوک مقداردهی ارجاع یافته است.
- در حالتی که سازنده ثانویهای تعریف کرده باشید، باید توجه کنید که بلوکهای مقداردهی تعریف شده پیش از اجرای بدنه سازنده ثانویه اجرا خواهند شد.
اینک که به سازندههای ثانویه اشاره کردیم، فرصت خوبی است که کمی در مورد آنها صحبت کنیم.
به دلیل فقدان آرگومانها در جاوا در اغلب موارد یک ضد الگو به صورت «سازندههای تلسکوپی» (Telescoping Constructor) میبینید که بارها و بارها overload میشوند و نوع ظاهر تلسکوپی در ادیتور یا نمودار کلاس پیدا میکنند. در ادامه مثال کوچکی از این ضد الگو میبینید:
1private String name;
2private String breed;
3private boolean registered;
4
5public Dog() {
6 this("Scruffy");
7}
8public Dog(String name) {
9 this(name, "Terrier");
10}
11public Dog(String name, String breed) {
12 this(name, breed, false);
13}
14public Dog(String name, String breed, boolean registered) {
15 this.name = name;
16 this.breed = breed;
17 this.registered = registered;
18}
با بهرهگیری از الگوی builder میتوانید از این وضعیت جلوگیری کنید، اما این کار در کاتلین غالباً غیرضروری است و میتوانید مقادیر پیشفرض را برای پارامترهای سازنده اعلان کنید:
1class Dog (val name: String,
2 val breed: String = "Terrier",
3 val registered: Boolean = false)
این گفته به آن معنی است که هنوز موارد زیادی وجود دارند که ممکن است متوجه شوید باید یک یا چند سازنده ثانویه را تعریف کنید.
برای نمونه فرض کنید یک کپی از سازنده وجود دارد که مقادیر یک وهله موجود از کلاس Dog را به وهله جدیدی از کلاس Dog کپی میکند:
1class Dog (val name: String) {
2 constructor(dog: Dog) : this(dog.name)
3}
4
5val dog01 = Dog(name = "Sparky")
6val dog02 = Dog(dog01)
نکاتی در خصوص سازندههای ثانویه
- در ابتدای آنها باید پیشوندی به صورت کلیدواژه constructor باشد.
- باید سازندههای اولیه را به صورت مستقیم یا غیرمستقیم از طریق سازنده ثانویه دیگری فراخوانی کنند. فراخوانی سازنده اولیه به وسیله کلیدواژه this چنان که در مثال فوق نشان دادیم صورت میگیرد.
- بلوکهای Initializer همواره پیش از بدنه هر سازنده ثانویه اجرا خواهند شد.
- پارامترهای تعیین شده در سازندههای ثانویه به خصوصیتها یا فیلدهای کلاس تبدیل نمیشوند. آنها نمیتوانند دارای پیشوندهای var یا val باشند. به بیان دیگر باید انتساب این مقادیر ارسالی به فیلدها یا هر کار دیگری که لازم است را در بدنه سازنده-(های) ثانویه اجرا کنید.
سخن پایانی
اگر دانش قبلی در مورد جاوا دارید، در این صورت ممکن است متوجه شوید که سازندهها در کاتلین در وهله نخست ممکن است کمی دشوار به نظر برسند. برای آموزش هر چه بیشتر در این خصوص به مستندات رسمی کالین (+) سر بزنید و مقدار زیادی تمرین کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای پروژه محور برنامه نویسی اندروید
- گنجینه برنامهنویسی اندروید (Android)
- مجموعه آموزشهای برنامهنویسی
- ساخت اپلیکیشن ضبط صدا با کاتلین (Kotlin) — به زبان ساده
- اسامی مستعار و کلاس های inline در کاتلین — به زبان ساده
==