در این بخش از مقاله آموزش کاتلین به بررسی «سازندهها» (constructors) در کاتلین میپردازیم. در این مسیر هر دو نوع سازنده اولیه و ثانویه را معرفی کرده و همچنین بلوکهای مقداردهی را به کمک مثالهایی مورد بررسی قرار میدهیم.
سازنده یا constructor یک روش منسجم برای مقداردهی مشخصههای کلاس محسوب میشود. سازنده یک تابع عضو خاص کلاس است که در زمان مقداردهی (ایجاد) شیء فراخوانی میشود. با این حال، طرز کار سازندهها در کاتلین نسبت به جاوا و زبانهای دیگر برنامهنویسی کمی متفاوت است.
در کاتلین دو نوع سازنده به شرح زیر داریم:
- سازنده اولیه: روش منسجمی برای مقداردهی یک کلاس است.
- سازنده ثانویه: امکان تعریف منطق اضافی برای مقداردهی را فراهم میسازد.
سازنده اولیه
سازنده اولیه بخشی از هدر کلاس محسوب میشود. به مثال زیر توجه کنید:
class Person(val firstName: String, var age: Int) { // class body
}
بلوک کدی که درون پرانتز احاطه شده، سازنده اولیه است. ساختار کلی آن به صورت زیر است:
(val firstName: String, var age: Int)
سازنده دو مشخصه اعلان میکند که یکی firstName است که یک مشخصه فقط-خواندنی است، زیرا با کلیدواژه val اعلان شده است و دیگری مشخصه 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” و 25 طوری به آن ارسال میشوند که گویی Person یک تابع است. این امر موجب مقداردهی مشخصههای firstName و age به ترتیب با مقادیر “Joe” و 25 میشود. روشهای دیگری نیز برای استفاده از سازندههای اولیه وجود دارد که در بخش بعدی توضیح میدهیم.
سازنده اولیه و بلوکهای مقداردهی
سازنده اولیه دارای یک ساختار مقید است و نمیتواند شامل هیچ کدی باشد. برای نوشتن کد مقداردهی اولیه (و نه فقط کدی که مشخصهها را مقداردهی کند) باید از یک بلوک مقداردهی استفاده کنیم. کلیدواژه ابتدایی این بلوک به صورت 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” و 25 به ترتیب در زمان ایجاد شیء person1 دریافت میشود. با این حال fName و personage بدون استفاده از var و val استفاده شدهاند و مشخصههای کلاس Person هستند.
کلاس Person دو مشخصه firstName و age را اعلان کرده است. زمانی که شیء person1 ایجاد میشود، کد درون بلوک مقداردهی اجرا میشود. بلوک مقداردهی نه تنها مشخصهها را مقداردهی اولیه میکند بلکه آنها را پرینت نیز میکند.
برای اجرای این وظیفه یک روش دیگر وجود دارد:
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 ساخته میشوند.
سازندههای ثانویه در کاتلین رایج نیستند. رایجترین کاربرد سازنده ثانویه در زمانی است که لازم است یک کلاس را بسط دهیم که چند سازنده دارد و کلاس را به روشهای متفاوتی بسط میدهد. این موضوع با بحث وراثت در کاتلین (+) مرتبط است که در ادامه بیشتر بررسی خواهیم کرد. روش ساخت سازنده ثانویه در کاتلین به شرح زیر است:
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
نکته: سازنده ثانویه باید کلاس مبنا را مقداردهی کند یا در صورتی که مانند مثال فوق، کلاس مورد نظر سازنده اولیه نداشته باشد، سازنده دیگری را «نمایندگی» (delegate) کند.