آبجکت (اشیا) و کلاس ها در کاتلین

توضیحات

با استفاده از ویژگی شی گرایی در برنامه نویسی راحت تر میتونید به توسعه ی اپ های بزرگ و رابط گرافیکی کاربری بپردازید.

معمولا آبجکت یک ماهیت از دنیای واقعی رو شبیه سازی میکنه مثلا یک دانش آموز، یک تلویزیون، یک دایره ، یک مستطیل و هر شی تو دنیای واقعی میتونه با یک آبجکت (object) شبیه سازی بشه.

به آبجکت ها، اشیا نیز گفته میشه اما کلمه ی آبجکت شناخته شده تر است.

باید متناسب با آبجکتی که میخوایم ایجاد کنیم یک کلاس تعریف کرده باشیم.

هرچندتا آبجکت که بخوایم میتونیم از یک کلاس ایجاد کنیم و هر آبجکت اطلاعات مختص به خودشو داره و از سایر آبجکت ها مستقله.

هنگامی که یک آبجکت از یک کلاس (class) ایجاد میکنیم میگیم یک نمونه از کلاس ایجاد کردیم.

ساختار کلاس

برای ایجاد آبجکت نیاز داریم کلاس (class) تعریف کنیم.

با استفاده از کلیدواژه ی class و اسم دلخواه برای کلاس میتونیم یک کلاس تعریف کنیم.

class Name{ ... }

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

ساختار کلاس در کاتلین
ساختار یک کلاس

۱- سازنده (constructor)

‎t0, t1, ..., tn پارامتر های کانستراکتور هستند

در اینجا یک کانستراکتور اولیه داریم بلوک init بدنه ی کانستراکتور های اولیه رو تشکیل میدن.

۲- متغیر ها

هرچندتا متغیر بخوایم میتونیم داخل یک کلاس تعریف کنیم.

متغیر های داخل کلاس به دیتا فیلد (data field) یا پراپرتی (property) معروف هستند.

۳- توابع

هر چندتا تابع بخوایم میتونیم داخل کلاس تعریف کنیم.

۴- آبجکت همراه (companion object)

داخل هر کلاس میتونیم فقط یک آبجکت همراه (companion object) داشته باشیم.

سازنده (constructor)

برای تعریف سازنده در کلاس کاتلین از کلیدواژه ی constructor استفاده میکنیم.

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

بدون صدا زدن کانستراکتور نمیتونیم از کلاس نمونه ایجاد کنیم.

در هر کلاس میتونیم یک یا چند کانستراکتور تعریف کنیم و برای ایجاد نمونه از کلاس هرکدومو خواستیم صدا کنیم.

در کاتلین اولین کانستراکتور که هنگام تعریف کلاس بعد از اسم کلاس وجود داره به کانستراکتور اولیه و بقیه کانستراکتور ها به کانستراکتور های ثانویه معروف هستند.

کانستراکتور اولیه

با استفاده از کلیدواژه ی constructor بعد از اسم کلاس میتونیم سازنده ی اولیه در کاتلین ایجاد کنیم.

class MyClass constructor(){ ... }

سازنده (constructor) که در بالا میبینیم به عنوان کنستراکتور اولیه (primary constructor) شناخته میشه. یک کانستراکتور اولیه و چند کانستراکتور ثانویه در کاتلین میتونیم داشته باشیم.

میشه از کلیدواژه های سطح دسترسی در هر کانستراکتوری که خواستیم استفاده کنیم.

class MyClass private constructor(){ }

بدنه ی کنستراکتور اولیه بلوک init است برای کانستراکتور اولیه در کاتلین هرچندتا بلوک init خواستیم میتونیم تعریف کنیم که به ترتیب موقع اجرای برنامه کد های داخلشون اجرا میشن.

class MyClass(){ init{ println("First Block") } init{ println("Second Block") } init{ println("Third Block") } }

میتونیم داخل بدنه ی کانستراکتور پارامتر های کانستراکتور رو به متغیر های کلاس اختصاص بدیم.

اگه قبل از کانستراکتور اولیه نخوایم کلیدواژه ی سطح دسترسی تعریف کنیم یا annotation بزاریم میتونیم از نوشتن کلید واژه ی constructor صرف نظر کنیم:

class MyClass constructor(){ ... }

در یک کلاس کانستراکتور اولیه به طور پیشفرض وجود داره حتی اگه آشکارا نوشته نشه.

class MyClass{ }

کانستراکتور های ثانویه

کانستراکتور های ثانویه (Secondary Constructors) رو داخل کلاس و مانند کانستراکتور اولیه با استفاده از کلیدواژه ی constructor تعریف میکنیم.

در تعریف کانستراکتور های ثانویه استفاده از کلیدواژه ی constructor همیشه لازمه.

بدنه ی کانستراکتور های ثانویه دنباله ی خودشونه و بلوکی مثل init براشون وجود نداره.

class TV{ constructor(size: Float, panel: String){ ... } }

اگه کانستراکتور اولیه آشکارا تعریف شده باشه، هرکدوم از کانستراکتور های ثانویه باید وکالت کانستراکتور اولیه رو مستقیم یا غیر مستقیم با استفاده از کلیدواژه ی this به عهده بگیرن.

به بیان دیگه کانستراکتور اولیه به صورت پیشفرض قبل از سایر کانستراکتور ها صدا زده میشه، اگه کانستراکتور اولیه آشکارا تعریف شده باشه، باید با استفاده از کلیدواژه ی this بعد از پرانتز آشکارا کانستراکتور اولیه رو قبل از کانستراکتور ثانویه صدا بزنیم.

//تعریف آشکار کانستراکتور اولیه بدون پارامتر class MyClass(){ //صدا زدن کانستراکتور اولیه با کلیدواژه ی this constructor(...): this(){ ... } }
class TV(size: Float){ constructor(size: Float, panel: String): this(size){ } }

میتونیم پارامتر های یکی از کانستراکتور های ثانویه که وکالت کانستراکتور اولیه رو به عهده داره با کلیدواژه ی this در کانستراکتور جدید مقداردهی کنیم، بدین ترتیب وکالت غیر مستقیم کانستراکتور اولیه رو به کانستراکتور جدید دادیم.

class TV(size: Float){ constructor(size: Float, panel: String): this(size){ } constructor(size: Float, panel: String, color: String): this(size, panel){ } }

ایجاد نمونه (آبجکت) از کلاس

برای ایجاد نمونه از کلاس کافیه یکی از کانستراکتور های کلاسو صدا بزنیم.

هنگامی که یک آبجکت (نمونه از کلاس) در حافظه ایجاد میشه متغیر به آدرس آبجکت در حافظه اشاره میکنه و خود متغیر حاوی مقداری نیست، به عبارتی متغیر نقش یک اشاره گر رو بازی میکنه.

مثال

میخوایم یک کلاس به اسم TV تعریف کنیم و ازش سه تا نمونه در تابع main ایجاد کنیم. در این مثال سه تا سازنده (کانستراکتور) در کلاس TV تعریف میکنیم و هر سه کانستراکتور ها رو به ترتیب در تابع main صدا میزنیم.

fun main(){ val tv0 = TV(21f) val tv1 = TV(42.5f, "LCD") val tv2 = TV(32f, "LED", "White") } class TV(size: Float){ constructor(size: Float, panel: String): this(size){ } constructor(size: Float, panel: String, color: String): this(size, panel){ } }

۲- متغیرهای کلاس (Properties)

میتونیم داخل کلاس متغیر تعریف و مقداردهی کنیم، به متغیر های داخل کلاس دیتا فیلد (field data) یا پراپرتی (property) میگیم. پراپرتی ها گویای وضعیت آبجکت هستند.

میتونیم به پراپرتی ها مقدار اولیه بدیم یا با پارمتر های کانستراکتور مقداردهیشون کنیم.

class TV(size: Float){ val size: Float var isOn: Boolean = false init { this.size = size } }

در کاتلین بدون نیاز به بلوک init میتونیم پارامتر های کانستراکتور اولیه رو مستقیم به پراپرتی ها اختصاص بدیم.

class TV(size: Float){ private val _size = size }

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

class TV(private val size: Float){ }

توابع setter و getter پراپرتی ها

برای هر پراپرتی دو تابع setter و getter در کاتلین به صورت پیشفرض تعریف شده وقتی بخوایم مقدار جدیدی به پراپرتی بدیم تابع set به طور پنهان صدا زده میشه؛ وقتی بخوایم از پراپرتی استفاده کنیم تابع get به طور پنهان صدا زده میشه.

تعریف آشکار setter و معرفی backing field

میتونیم تابع setter رو برای هر پراپرتی به طور آشکار پیاده سازی کنیم.

class TV{ var channel: Int = 1 set(value){ if(value >= 1 && value <= 120 ){ field = value } } }

در بالا value یک اسم دلخواه برای پارامتر تابع set است و field داخل تابع set به طور خودکار توسط کاتلین ایجاد شده و مقدار پراپرتی در حافظه رو نگه داری میکنه. بهش بکینگ فیلد (backing field) میگیم.

میتونیم با استفاده از کلیدواژه ی private از دسترسی به تابع setter در بیرون کلاس جلوگیری کنیم.

class TV(){ var isOn: Boolean = false private set }

تعریف آشکار getter

میتونیم تابع getter رو برای هر متغیری که خواستیم، به طور آشکار تعریف کنیم.

class MyClass{ var prop: T = ... get(){ ... return t } }

اگه مقداری که getter بر میگردونه به صورت تک عبارتی و متغیر تغییرناپذیر (val) باشه، میتونیم getter رو به فرم زیر تعریف کنیم:

class MyClass{ var prop: T get() = t }

معرفی بکینگ پراپرتی (Backing Property)

اگه متغیر تغییرپذیر (var) باشه و تابع setter پنهان باشه و به هر دلیلی نخواید مقدار متغیر در بیرون از کلاس تغییر کنه میتونید متغیر رو به صورت بکینگ پراپرتی (Backing Property) پیاده سازی کنید.

در این روش با استفاده از کلیدواژه ی سطح دسترسی private از دسترسی به متغیر اصلی در خارج از کلاس جلوگیری میکنیم و با تعریف یک متغیر به عنوان نماینده ی متغیر اصلی به متغیر اصلی در خارج از کلاس با getter دسترسی میدیم. به متغیر اصلی بکینگ پراپرتی (backing property) میگیم.

class TV(){ private var _volumeLevel: Int = 0 val volumeLevel: Int get() = _volumeLevel }

صدا زدن پراپرتی

برای صدا زدن پراپرتی در خارج از کلاس کافیه اسم متغیری که به آبجکت اشاره داره رو بنویسیم، نقطه بزاریم و اسم پراپرتی مورد نظر در آبجکت رو بنویسیم.

fun main(){ val tv = TV() println("is tv on? ${tv.isOn}") tv.isOn = true println("is tv on? ${tv.isOn}") } class TV{ var isOn = false }

در داخل کلاس هنگامی که متغیر عضو کلاس با یک متغیر دیگه که عضو کلاس هم نام باشه؛ با کلیدواژه ی this در کاتلین باید مشخص کنیم کدوم متغیر عضو کلاس است.

class TV(private val size){ private val panel: String constructor(size, panel){ this.panel = panel } }

توابع کلاس

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

توابع در کلاس نیز از قوانین سطح دسترسی پیروی میکنند.

رفتار (behavior) یک آبجکت با توابع تعریف میشه. وقتی تابع مربوط به آبجکت رو صدا میزنیم تابع از آبجکت میخواد کاری رو انجام بده، مثلا در بخش پراپرتی ها دیدیم تابع getter مقدار پراپرتی رو بر میگردونه یا تابع setter مقدار جدید به پراپرتی اضافه میکنه.

class TV{ var isOn = false private set fun turnOn(){ isOn = true } }

صدا زدن تابع کلاس

برای صدا زدن توابع کلاس مانند پراپرتی ها اسم متغیری که به آبجکت اشاره داره رو مینویسیم نقطه میزاریم و سپس اسم تابع مورد نظر رو مینویسیم.

fun main(){ val tv = TV() tv.turnOn() }

آبجکت همراه

آبجکت همراه (companion object) در کاتلین از خانواده ی آبجکت های اعلامی است.

با تعریف آبجکت همراه (companion object) در کلاس میتونیم پراپرتی و توابع مستقل از آبجکت های کلاس تعریف کنیم.

پراپرتی و توابع تعریف شده در companion object مستقل از آبجکت های ایجاد شده از کلاس هستند و مقدار پراپرتی های companion object بین تمام آبجکت های ایجاد شده از کلاس مشترک است.

عموما اعضای companion object رو با دیتا فیلد (data field) و توابع استاتیک در جاوا یکی میدونن با اینکه اعضای companion object چیزی شبیه به اعضای استاتیک در جاوا هستند اما در واقع اعضای companion object استاتیک نیستند.

آبجکت های همراه نوعی سینگلتون (singlton) هستند که همراه با کلاس ایجاد شده و مستقل از آبجکت های ایجاد شده از کلاسند.

fun main(){ val first = MyClass() first.printCurrentObjectIndex() val second = MyClass() second.printCurrentObjectIndex() val third = MyClass() third.printCurrentObjectIndex() val fourth = MyClass() fourth.printCurrentObjectIndex() MyClass.printTotalNumberOfCreatedObjects() } class MyClass{ var index init{ index = count++ } fun printCurrentObjectIndex(){ println("Index of current object is $index") companion object Counter{ var count = 0 fun printTotalNumberOfCreatedObjects(){ println("Total number of objects created by MyClass are $count") } } }

اسم Counter اسمی دلخواه برای companion object است.

میتونیم آبجکت های همراه رو نامگذاری نکنیم در این صورت برای صدا زدن اعضای آبجکت همراه باید اسم کلاس نقطه، اسم Companion نقطه و اسم عضو مورد نظر رو بنویسیم:

fun main(){ val first = MyClass() first.printCurrentObjectIndex() val second = MyClass() second.printCurrentObjectIndex() val third = MyClass() third.printCurrentObjectIndex() val fourth = MyClass() fourth.printCurrentObjectIndex() MyClass.Companion.printTotalNumberOfCreatedObjects() } class MyClass{ var index init{ index = count++ } fun printCurrentObjectIndex(){ println("Index of current object is $index") companion object Counter{ var count = 0 fun printTotalNumberOfCreatedObjects(){ println("Total number of objects created by MyClass are $count") } } }

مورد مطالعه (مثال ها)

خب الان وقتشه مثال هایی از کلاس و آبجکت ببینیم.

تعریف کلاس تلویزیون و ایجاد نمونه از کلاس.

از قسمت های کلاس TV در بالا برای توضیحات استفاده کردیم الان وقتشه این قسمت ها رو یکجا در قالب یک مثال کامل بیاریم.

fun main(){ val tv0 = TV(21f) tv0.turnOn() tv0.printBasicInfo() tv0.channel = 25 tv0.volumeUp() println("tv0's channel is ${tv0.channel} and volume level is ${tv0.volumeLevel}") val tv1 = TV(42.5f, "LCD", "Silver") tv1.turnOn() tv1.printBasicInfo() tv1.channel = 12 tv1.volumeUp() tv1.volumeUp() tv1.volumeUp() println("tv1's channel is ${tv1.channel} and volume level is ${tv1.volumeLevel}") } class TV(private val size: Float){ private var panel: String = "Flatiron" private var color: String = "Black" var isOn: Boolean = false private set var channel: Int = 1 set(value){ if(value > 0 && value <= 120) //Backing Field field = value } //Backing Property private var _volumeLevel: Int = 1 val volumeLevel get() = _volumeLevel constructor(size: Float, panel: String): this(size){ this.panel panel } constructor(size: Float, panel: String, color: String): this(size, panel){ this.color = color } fun turnOn(){ isOn = true } fun turnOff(){ isOn = false } fun channelUp(){ if(isOn && channel < 120) channel++ } fun channelDown(){ if(isOn && channel > 1) channel-- } fun volumeUp(){ if(isOn && volumeLevel < 100) _volumeLevel++ } fun volumeDown(){ if(isOn && volumeLevel > 0) _volumeLevel-- } fun printBasicInfo(){ println("Television:\nsize: $size\" \npanel: $panel \ncolor: $color") } }

تعریف کلاس مستطیل و ایجاد نمونه از کلاس.

طول و عرض مستطیل ویژگی های مستطیل هستند، مساحت و محیط مستطیل از طول و عرض مستطیل محاسبه میشه بنابراین دوتا پراپرتی برای طول و عرض داریم و دو تابع برای مساحت و محیط مستطیل.

fun main(){ val r0 = Rectangle() println("Rectangle r0: \n width: ${r0.width} \n height: ${r0.height} \n area: ${r0.area()} \n perimeter: ${r0.perimeter()}") val r1 = Rectangle(4, 8) println("Rectangle r1: \n width: ${r1.width} \n height: ${r1.height} \n area: ${r1.area()} \n perimeter: ${r1.perimeter()}") } class Rectangle(var width: Double = 1.0, var height: Double = 1.0){ fun area(): Double = width * height fun perimeter() = 2 * (width + height) }

تعریف کلاس دایره و ایجاد نمونه از کلاس.

دایره یه شکل هندسیه و محاسبات مساحت و محیط دایره بستگی به مقدار شعاع دایره داره. در این مثال برای محاسبه ی مساحت و محیط بجای تابع از پراپرتی val با getter استفاده میکنیم وقتی پراپرتی تغییر ناپذیر با getter تعریف میشه نقشی شبیه به تابع رو ایفا میکنه.

fun main(){ val c0 = Circle(2) println("c0's radius is ${c0.radius} so the area is ${c0.area}, and the perimeter is ${c0.perimeter}) c0.radius = 4 println("Radius is changed to ${c0.radius} so the Area is ${c0.area}, and the Perimeter is ${c0.perimeter}) val c1 = Circle(2) println("c1's radius is ${c1.radius} so the Area is ${c1.area}, and the perimeter is ${c1.perimeter}) } class Circle(){ var radius: Double = 1.0 val area: Double get() = radius * radius * PI var perimeter: Double get() = 2 * radius * PI constructor(radius: Double){ this.radius = radius } }

خلاصه

- برای ایجاد آبجکت نیاز به کلاس داریم

- کانستراکتور ها نوع خاصی از تابع هستند که هیچ مقداریو بر نمیگردونن

- هنگام ایجاد یک آبجکت (نمونه) از کلاس یکی از کانستراکتور های کلاس صدا زده میشه.

- پراپرتی های کلاس حاوی اطلاعات کلاس هستند و تعیین کننده ی وضعیت کلاس اند.

- دو تابع getter و setter در کاتلین به صورت پنهان برای هر پراپرتی تعریف میشه.

- با استفاده از کلیدواژه ی this در داخل کلاس میتونیم رجوع کنیم به پراپرتی های کلاس.

- توابع در کلاس، رفتار آبجکت رو تعیین میکنن و بهش میگن چکار کنه.

- با اینکه اعضای آبجکت همراه (companion object) استاتیک نیستند اما شبیه اعضای استاتیک در جاوا عمل میکنن.

arrow_drop_up
کپی شد!