سازنده متدی است که از آن برای مقداردهی پراپرتیهای یک کلاس استفاده میشود. فرق این تابع با بقیۀ توابع عضو این است که به محض نمونهسازی از کلاس، تابع فوق به صورت خودکار فرخوانی شده و پراپرتیها را مقداردهی اولیه میکند. اما در کاتلین کمی ماجرا فرق میکند.
در کاتلین دو سازنده داریم:
- سازندۀ اولیه (Primary Constructor): مقداردهی اولیۀ کلاس
- سازندۀ ثانویه (Secondary Constructor): افزودن منطق اضافی در ارتباط با مقداردهی اولیۀ کلاس
سازندۀ اولیه
سازندۀ اولیه بخشی از هیدر کلاس محسوب میشود:
class Person(val firstName: String, var age: Int) { // class body }
بلاکی از کد که با پرانتز احاطه شده یک سازندۀ اولیه است: (Person(val firstName: String, var age: Int.
سازنده دو پراپرتی تعریف کرده است: firstName (این پراپرتی چون از کلیدواژۀ val استفاده کرده، فقط خواندنی یا readonly است) و دیگری age (که با var تعریف شده یعنی خواندنی-نوشتنی است).
مثالی از سازندۀ اولیه
fun main(args: Array<String>) { val person1 = Person("Joe", 25) println("First Name = ${person1.firstName}") println("Age = ${person1.age}") } class Person(val firstName: String, var age: Int) { }
خروجی:
First Name = Joe Age = 25
در هنگام ساخت آبجکتی از کلاس Person مقادیر Joe و ۲۵ را همچون یک تابع به آن ارسال میکنیم. این مقادیر توسط سازنده دریافت شده و مقداردهی لازم صورت میگیرد؛ یعنی firstName از آبجکت person1 به Joe مقداردهی شده و age نیز ۲۵٫ راههای دیگری نیز برای استفاده از سازندههای اولیه وجود دارد.
سازندۀ اولیه و بلاک مقداردهی اولیه
سازندۀ اولیه محدود بوده و نمیتوانند شامل کدی باشد. برای اضافه کردن کدهای بیشتر از initializer block استفاده میشود. این بلاک با کلمۀ کلیدی init شروع میشود. بیایید کد بالا را کمی اصلاح کنیم:
fun main(args: Array<String>) { val person1 = Person("joe", 25) } class Person(fName: String, personAge: Int) { val firstName: String var age: Int // initializer block init { firstName = fName.capitalize() age = personAge println("First Name = $firstName") println("Age = $age") } }
خروجی:
First Name = Joe Age = 25
اینجا پارامترهای fName و personAge داخل پرانتز مقادیر Joe و ۲۵ را برای شی person1 دریافت میکنند. با این حال این دو بدون استفاده از var یا val استفاده شدهاند و پراپرتیهایی از کلاس Person نیستند. کلاس Person، دو پراپرتی firstName و age دارد. وقتی person1 ساخته میشود، کدهای داخل بلاک init اجرا شده و علاوه بر مقداردهی پراپرتیها، مقادیرشان را نیز چاپ میکند.
همین کار را طور دیگری نیز میتوان انجام داد:
fun main(args: Array<String>) { val person1 = Person("joe", 25) } class Person(fName: String, personAge: Int) { val firstName = fName.capitalize() var age = personAge // initializer block init { println("First Name = $firstName") println("Age = $age") } }
برای تفکیک پارامترهای سازنده از پراپرتیها، نامهای متفاوتی استفاده شدهاند (fName و firstName و personAge و age). البته رایجتر است که به جای استفاده از نامهایی کاملاً متفاوت تفاوت را با خط زیر مشخص میکنند. مثلاً firstName_ و age_ :
class Person(_firstName: String, _age: Int) { val firstName = _firstName.capitalize() var age = _age // initializer block init { ... .. ... } }
مقدار پیشفرض در سازندۀ اولیه
همانند سایر فانکشنها برای سازنده نیز میتوان مقادیر پیشفرض مشخص نمود. برای مثال:
fun main(args: Array<String>) { println("person1 is instantiated") val person1 = Person("joe", 25) println("person2 is instantiated") val person2 = Person("Jack") println("person3 is instantiated") val person3 = Person() } class Person(_firstName: String = "UNKNOWN", _age: Int = 0) { val firstName = _firstName.capitalize() var age = _age // initializer block init { println("First Name = $firstName") println("Age = $age\n") } }
خروجی:
First Name = Joe Age = 25 person2 is instantiated First Name = Jack Age = 0 person3 is instantiated First Name = UNKNOWN Age = 0
سازندۀ ثانویه در کاتلین
در کاتلین، یک کلاس میتواند یک یا بیشتر سازندۀ ثانویه هم داشته باشد. این سازندهها با کلمهی کلیدی constructor ساخته میشوند. سازندهای ثانویه چندان در کاتلین رایج نیستند. مهمترین کاربرد سازندهی ثانویه برای زمانی است که شما نیازمند extend کردن یک کلاس هستید. برای اینکار سازندههای مختلفی برای مقداردهی به روشهای مختلف ساخته میشود. البته برای درک بهتر این بحث ابتدا باید با مفهوم ارثبری آشنا باشید که در درسهای بعدی به آن خواهم پرداخت ولی فعلاً از روی کدها میتوانید مفهوم سازندههای ثانویه را درک کنید:
class Log { constructor(data: String) { // some code } constructor(data: String, numberOfData: Int) { // some code } }
کلاس Log در اینجا دارای دو سازندۀ ثانویه بوده و سازندۀ اولیه هم ندارد. حالا بیایید این کلاس را اکستند کنیم:
class Log { constructor(data: String) { // code } constructor(data: String, numberOfData: Int) { // code } } class AuthLog: Log { constructor(data: String): super(data) { // code } constructor(data: String, numberOfData: Int): super(data, numberOfData) { // code } }
اینجا، سازندههای کلاس مشتق شده یعنی AuthLog سازندههای ثانویه کلاسی که از آن اکستند شده (Log) را فرخوانی میکند. به همین خاطر از ()super استفاده شده که به کلاس اصلی اشاره دارد.
در کاتلین نیز همانند جاوا میتوانید از داخل سازندهای، سازندۀ دیگری را فراخونی کنید. این کار را با کلمهی this انجام میدهیم.
class AuthLog: Log { constructor(data: String): this(data, 10) { // code } constructor(data: String, numberOfData: Int): super(data, numberOfData) { // code } }
مثالی از سازندههای ثانویه در کاتلین
fun main(args: Array<String>) { val p1 = AuthLog("Bad Password") } open class Log { var data: String = "" var numberOfData = 0 constructor(_data: String) { } constructor(_data: String, _numberOfData: Int) { data = _data numberOfData = _numberOfData println("$data: $numberOfData times") } } class AuthLog: Log { constructor(_data: String): this("From AuthLog -> " + _data, 10) { } constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) { } }
خروجی:
From AuthLog -> Bad Password: 10 times
نکته: در صورتی که کلاس، سازندۀ اولیه نداشته باشد، سازندۀ ثانویه باید کلاس پایه را مقداردهی کرده یا این کار را به سازندۀ دیگری واگذار کند (همانند مثال بالا).
بدون دیدگاه