Kotlin Bootcamp for Programmers 4: برنامه نویسی شی گرا

این Codelab بخشی از دوره Kotlin Bootcamp برای برنامه نویسان است . اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. بسته به دانش خود، ممکن است بتوانید برخی از بخش ها را مرور کنید. این دوره برای برنامه نویسانی است که زبان شی گرا را می دانند و می خواهند Kotlin را یاد بگیرند.

مقدمه

در این کد لبه، شما یک برنامه Kotlin ایجاد می کنید و با کلاس ها و اشیاء در Kotlin آشنا می شوید. اگر زبان شی گرا دیگری بلد باشید، بیشتر این محتوا برای شما آشنا خواهد بود، اما کاتلین تفاوت های مهمی برای کاهش مقدار کد مورد نیاز برای نوشتن دارد. شما همچنین در مورد کلاس های انتزاعی و تفویض رابط کاربری یاد می گیرید.

به جای ساختن یک برنامه نمونه واحد، درس‌های این دوره برای ایجاد دانش شما طراحی شده‌اند، اما نیمه مستقل از یکدیگر باشند تا بتوانید بخش‌هایی را که با آن‌ها آشنا هستید، مرور کنید. برای گره زدن آنها به یکدیگر، بسیاری از نمونه ها از تم آکواریوم استفاده می کنند. و اگر می خواهید داستان کامل آکواریوم را ببینید، دوره Kotlin Bootcamp for Programmers Udacity را بررسی کنید.

آنچه از قبل باید بدانید

  • اصول اولیه Kotlin، از جمله انواع، عملگرها و حلقه زدن
  • نحو تابع کاتلین
  • اصول برنامه نویسی شی گرا
  • اصول اولیه یک IDE مانند IntelliJ IDEA یا Android Studio

چیزی که یاد خواهید گرفت

  • نحوه ایجاد کلاس ها و دسترسی به خصوصیات در Kotlin
  • نحوه ایجاد و استفاده از سازنده کلاس در Kotlin
  • نحوه ایجاد یک زیر کلاس و نحوه عملکرد وراثت
  • درباره کلاس‌های انتزاعی، رابط‌ها، و تفویض رابط
  • نحوه ایجاد و استفاده از کلاس های داده
  • نحوه استفاده از کلاس های سینگلتون، enums و sealed

کاری که خواهی کرد

  • یک کلاس با خصوصیات ایجاد کنید
  • یک سازنده برای یک کلاس ایجاد کنید
  • یک زیر کلاس ایجاد کنید
  • نمونه هایی از کلاس ها و رابط های انتزاعی را بررسی کنید
  • یک کلاس داده ساده ایجاد کنید
  • در مورد کلاس های تکی، enums، و کلاس های مهر و موم شده بیاموزید

اصطلاحات برنامه نویسی زیر باید قبلاً برای شما آشنا باشد:

  • کلاس ها نقشه هایی برای اشیا هستند. به عنوان مثال، یک کلاس Aquarium طرحی برای ساخت یک شی آکواریوم است.
  • اشیا نمونه هایی از کلاس ها هستند. یک شی آکواریوم یک Aquarium واقعی است.
  • ویژگی ها ویژگی های طبقات هستند، مانند طول، عرض و ارتفاع یک Aquarium .
  • متدها که توابع عضو نیز نامیده می شوند، عملکرد کلاس هستند. روش‌ها کارهایی هستند که می‌توانید با آن شی «انجام دهید». به عنوان مثال، می‌توانید یک شیء Aquarium fillWithWater() را پر کنید.
  • واسط مشخصاتی است که یک کلاس می تواند پیاده سازی کند. به عنوان مثال، تمیز کردن برای اشیاء غیر از آکواریوم رایج است و تمیز کردن عموماً به روش های مشابه برای اشیاء مختلف انجام می شود. بنابراین می توانید یک رابط به نام Clean داشته باشید که متد clean() را تعریف می کند. کلاس Aquarium می تواند رابط Clean را برای تمیز کردن آکواریوم با یک اسفنج نرم پیاده سازی کند.
  • بسته ها راهی برای گروه بندی کدهای مرتبط به منظور سازماندهی نگه داشتن آن یا ایجاد کتابخانه ای از کدها هستند. پس از ایجاد یک بسته، می توانید محتویات بسته را به فایل دیگری وارد کنید و از کد و کلاس های موجود در آن مجددا استفاده کنید.

در این کار یک پکیج جدید و یک کلاس با چند ویژگی و متد ایجاد می کنید.

مرحله 1: یک بسته ایجاد کنید

بسته ها می توانند به شما کمک کنند تا کد خود را سازماندهی کنید.

  1. در قسمت Project ، در زیر پروژه Hello Kotlin ، روی پوشه src کلیک راست کنید.
  2. New > Package را انتخاب کنید و آن را example.myapp .

مرحله 2: یک کلاس با ویژگی ها ایجاد کنید

کلاس ها با کلمه کلیدی class تعریف می شوند و نام کلاس ها بر اساس قرارداد با یک حرف بزرگ شروع می شود.

  1. روی بسته example.myapp راست کلیک کنید.
  2. New > Kotlin File / Class را انتخاب کنید.
  3. در قسمت Kind ، Class را انتخاب کنید و نام کلاس را Aquarium . IntelliJ IDEA نام بسته را در فایل گنجانده و یک کلاس Aquarium خالی برای شما ایجاد می کند.
  4. در داخل کلاس Aquarium ، ویژگی های var را برای عرض، ارتفاع و طول (به سانتی متر) تعریف و مقداردهی اولیه کنید. خصوصیات را با مقادیر پیش فرض مقداردهی اولیه کنید.
package example.myapp

class Aquarium {
    var width: Int = 20
    var height: Int = 40
    var length: Int = 100
}

در زیر هود، Kotlin به طور خودکار دریافت کننده ها و تنظیم کننده ها را برای ویژگی هایی که در کلاس Aquarium تعریف کرده اید ایجاد می کند، بنابراین می توانید مستقیماً به ویژگی ها دسترسی داشته باشید، به عنوان مثال myAquarium.length .

مرحله 3: یک تابع main() ایجاد کنید

یک فایل جدید به نام main.kt ایجاد کنید تا تابع main() را نگه دارید.

  1. در قسمت Project در سمت چپ، روی بسته example.myapp راست کلیک کنید.
  2. New > Kotlin File / Class را انتخاب کنید.
  3. در زیر منوی کشویی Kind ، انتخاب را به عنوان File نگه دارید و نام فایل را main.kt . IntelliJ IDEA شامل نام بسته است، اما تعریف کلاس برای یک فایل را شامل نمی شود.
  4. یک buildAquarium() تعریف کنید و در داخل یک نمونه از Aquarium ایجاد کنید. برای ایجاد یک نمونه، کلاس را طوری ارجاع دهید که گویی یک تابع است، Aquarium() . این سازنده کلاس را فراخوانی می کند و نمونه ای از کلاس Aquarium ایجاد می کند، مشابه استفاده از new در زبان های دیگر.
  5. یک تابع main() تعریف کنید و buildAquarium() را فراخوانی کنید.
package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium()
}

fun main() {
    buildAquarium()
}

مرحله 4: یک روش اضافه کنید

  1. در کلاس Aquarium ، روشی برای چاپ مشخصات ابعاد آکواریوم اضافه کنید.
    fun printSize() {
        println("Width: $width cm " +
                "Length: $length cm " +
                "Height: $height cm ")
    }
  1. در main.kt ، در buildAquarium() ، printSize() را در myAquarium کنید.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
}
  1. برنامه خود را با کلیک بر روی مثلث سبز رنگ کنار تابع main() اجرا کنید. نتیجه را رعایت کنید.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
  1. در buildAquarium() کدی را اضافه کنید تا ارتفاع را روی 60 تنظیم کنید و مشخصات ابعاد تغییر یافته را چاپ کنید.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
    myAquarium.height = 60
    myAquarium.printSize()
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 100 cm Height: 60 cm 

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

مرحله 1: یک سازنده ایجاد کنید

در این مرحله یک سازنده به کلاس Aquarium که در اولین وظیفه ایجاد کردید اضافه می کنید. در مثال قبلی، هر نمونه از Aquarium با همان ابعاد ساخته شده است. می‌توانید بعد از ایجاد آن با تنظیم ویژگی‌ها، ابعاد را تغییر دهید، اما ساده‌تر است که برای شروع اندازه آن را درست کنید.

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

  1. در کلاس Aquarium که قبلا ایجاد کردید، تعریف کلاس را طوری تغییر دهید که شامل سه پارامتر سازنده با مقادیر پیش فرض length ، width و height باشد و آنها را به ویژگی های مربوطه اختصاص دهید.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
   // Dimensions in cm
   var length: Int = length
   var width: Int = width
   var height: Int = height
...
}
  1. روش فشرده‌تر Kotlin این است که ویژگی‌ها را مستقیماً با سازنده، با استفاده از var یا val تعریف کنیم، و Kotlin همچنین گیرنده‌ها و تنظیم‌کننده‌ها را به‌طور خودکار ایجاد می‌کند. سپس می توانید تعاریف ویژگی را در بدنه کلاس حذف کنید.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
  1. هنگامی که یک شی Aquarium با آن سازنده ایجاد می کنید، می توانید هیچ آرگومانی را مشخص نکنید و مقادیر پیش فرض را دریافت کنید، یا فقط تعدادی از آنها را مشخص کنید، یا همه آنها را مشخص کنید و یک Aquarium با اندازه کاملا سفارشی ایجاد کنید. در تابع buildAquarium() ، راه های مختلف ایجاد یک شی Aquarium را با استفاده از پارامترهای نامگذاری شده امتحان کنید.
fun buildAquarium() {
    val aquarium1 = Aquarium()
    aquarium1.printSize()
    // default height and length
    val aquarium2 = Aquarium(width = 25)
    aquarium2.printSize()
    // default width
    val aquarium3 = Aquarium(height = 35, length = 110)
    aquarium3.printSize()
    // everything custom
    val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
    aquarium4.printSize()
}
  1. برنامه را اجرا کنید و خروجی را مشاهده کنید.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 25 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 110 cm Height: 35 cm 
Width: 25 cm Length: 110 cm Height: 35 cm 

توجه داشته باشید که لازم نیست سازنده را بیش از حد بارگذاری کنید و برای هر یک از این موارد یک نسخه متفاوت بنویسید (به علاوه چند نسخه دیگر برای ترکیب های دیگر). Kotlin آنچه را که از مقادیر پیش‌فرض و پارامترهای نام‌گذاری شده مورد نیاز است ایجاد می‌کند.

مرحله 2: بلوک های init را اضافه کنید

سازنده های مثال بالا فقط ویژگی ها را اعلام می کنند و مقدار یک عبارت را به آنها اختصاص می دهند. اگر سازنده شما نیاز به کد اولیه بیشتری داشته باشد، می توان آن را در یک یا چند بلوک init قرار داد. در این مرحله چند بلوک init را به کلاس Aquarium اضافه می کنید.

  1. در کلاس Aquarium ، یک بلوک init برای چاپ که شی در حال تنظیم اولیه است، و یک بلوک دوم برای چاپ حجم به لیتر اضافه کنید.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
    init {
        println("aquarium initializing")
    }
    init {
        // 1 liter = 1000 cm^3
        println("Volume: ${width * length * height / 1000} l")
    }
}
  1. برنامه را اجرا کنید و خروجی را مشاهده کنید.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm 
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm 

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

مرحله 3: درباره سازنده های ثانویه بیاموزید

در این مرحله با سازنده های ثانویه آشنا می شوید و یکی را به کلاس خود اضافه می کنید. علاوه بر سازنده اولیه، که می‌تواند یک یا چند بلوک اولیه داشته باشد، یک کلاس init می‌تواند یک یا چند سازنده ثانویه نیز داشته باشد تا امکان اضافه بار سازنده را فراهم کند، یعنی سازنده‌هایی با آرگومان‌های مختلف.

  1. در کلاس Aquarium ، یک سازنده ثانویه اضافه کنید که با استفاده از کلمه کلیدی constructor ، تعدادی ماهی را به عنوان آرگومان خود در نظر بگیرد. یک خاصیت val tank برای حجم محاسبه شده آکواریوم در لیتر بر اساس تعداد ماهی ایجاد کنید. فرض کنید 2 لیتر (2000 سانتی متر 3) آب به ازای هر ماهی، به علاوه کمی فضای اضافی تا آب نریزد.
constructor(numberOfFish: Int) : this() {
    // 2,000 cm^3 per fish + extra room so water doesn't spill
    val tank = numberOfFish * 2000 * 1.1
}
  1. در داخل سازنده ثانویه، طول و عرض (که در سازنده اولیه تنظیم شده است) را ثابت نگه دارید و ارتفاع مورد نیاز برای ایجاد حجم داده شده مخزن را محاسبه کنید.
    // calculate the height needed
    height = (tank / (length * width)).toInt()
  1. در تابع buildAquarium() یک فراخوان برای ایجاد یک Aquarium با استفاده از سازنده ثانویه جدید خود اضافه کنید. اندازه و حجم را چاپ کنید.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

توجه داشته باشید که حجم دو بار چاپ می شود، یک بار توسط بلوک init در سازنده اولیه قبل از اجرای سازنده ثانویه، و یک بار توسط کد موجود در buildAquarium() .

شما می توانستید کلمه کلیدی constructor را نیز در سازنده اصلی قرار دهید، اما در بیشتر موارد ضروری نیست.

مرحله 4: یک گیرنده ویژگی جدید اضافه کنید

در این مرحله یک گیرنده خصوصیت واضح اضافه می کنید. کاتلین هنگام تعریف ویژگی ها به طور خودکار گیرنده ها و تنظیم کننده ها را تعریف می کند، اما گاهی اوقات مقدار یک ویژگی نیاز به تنظیم یا محاسبه دارد. به عنوان مثال، در بالا، حجم Aquarium را چاپ کردید. شما می توانید حجم را به عنوان یک ویژگی با تعریف یک متغیر و یک گیرنده برای آن در دسترس قرار دهید. از آنجایی که volume باید محاسبه شود، دریافت کننده باید مقدار محاسبه شده را برگرداند که می توانید با یک تابع یک خطی این کار را انجام دهید.

  1. در کلاس Aquarium یک ویژگی Int به نام volume تعریف کنید و یک متد get() که حجم را در خط بعدی محاسبه می کند تعریف کنید.
val volume: Int
    get() = width * height * length / 1000  // 1000 cm^3 = 1 l
  1. بلوک init که حجم را چاپ می کند را بردارید.
  2. کد موجود در buildAquarium() که حجم را چاپ می کند را حذف کنید.
  3. در printSize() یک خط برای چاپ حجم اضافه کنید.
fun printSize() {
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm "
    )
    // 1 l = 1000 cm^3
    println("Volume: $volume l")
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ aquarium initializing
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

ابعاد و حجم مانند قبل است، اما حجم فقط یک بار پس از مقداردهی اولیه شی توسط سازنده اولیه و سازنده ثانویه چاپ می شود.

مرحله 5: یک تنظیم کننده ویژگی اضافه کنید

در این مرحله یک تنظیم کننده ویژگی جدید برای حجم ایجاد می کنید.

  1. در کلاس Aquarium ، volume را به var تغییر دهید تا بتوان آن را بیش از یک بار تنظیم کرد.
  2. با افزودن متد set() در زیر گیرنده که ارتفاع را بر اساس مقدار آب ارائه شده مجدداً محاسبه می کند، یک تنظیم کننده برای ویژگی volume اضافه کنید. طبق قرارداد، نام پارامتر تنظیم کننده value است، اما در صورت تمایل می توانید آن را تغییر دهید.
var volume: Int
    get() = width * height * length / 1000
    set(value) {
        height = (value * 1000) / (width * length)
    }
  1. در buildAquarium() کد اضافه کنید تا حجم آکواریوم را روی 70 لیتر تنظیم کنید. اندازه جدید را چاپ کنید.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    aquarium6.volume = 70
    aquarium6.printSize()
}
  1. دوباره برنامه خود را اجرا کنید و ارتفاع و حجم تغییر یافته را مشاهده کنید.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm 
Volume: 70 l

تاکنون هیچ اصلاح کننده دید، مانند public یا private ، در کد وجود نداشته است. به این دلیل که به طور پیش‌فرض، همه چیز در Kotlin عمومی است، به این معنی که همه چیز در همه جا قابل دسترسی است، از جمله کلاس‌ها، متدها، ویژگی‌ها و متغیرهای عضو.

در کاتلین، کلاس‌ها، اشیا، رابط‌ها، سازنده‌ها، توابع، ویژگی‌ها و تنظیم‌کننده‌های آنها می‌توانند اصلاح‌کننده‌های دید داشته باشند:

  • public یعنی خارج از کلاس قابل مشاهده است. همه چیز به طور پیش فرض عمومی است، از جمله متغیرها و متدهای کلاس.
  • internal به این معنی است که فقط در آن ماژول قابل مشاهده خواهد بود. یک ماژول مجموعه ای از فایل های Kotlin است که با هم کامپایل شده اند، به عنوان مثال، یک کتابخانه یا برنامه.
  • private به این معنی است که فقط در آن کلاس (یا فایل منبع اگر با توابع کار می کنید) قابل مشاهده است.
  • protected همان private است، اما برای هر زیر کلاسی نیز قابل مشاهده خواهد بود.

برای اطلاعات بیشتر به Visibility Modifiers در مستندات Kotlin مراجعه کنید.

متغیرهای عضو

ویژگی های یک کلاس یا متغیرهای عضو به طور پیش فرض public هستند. اگر آنها را با var تعریف کنید، قابل تغییر هستند، یعنی قابل خواندن و نوشتن هستند. اگر آنها را با val تعریف کنید، پس از مقداردهی اولیه فقط خواندنی هستند.

اگر خصوصیتی می‌خواهید که کد شما بتواند بخواند یا بنویسد، اما کد خارجی فقط بتواند بخواند، می‌توانید ویژگی و دریافت‌کننده آن را عمومی بگذارید و تنظیم‌کننده را خصوصی اعلام کنید، مانند شکل زیر.

var volume: Int
    get() = width * height * length / 1000
    private set(value) {
        height = (value * 1000) / (width * length)
    }

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

در کاتلین، به طور پیش‌فرض، کلاس‌ها را نمی‌توان زیر کلاس‌بندی کرد. به طور مشابه، ویژگی‌ها و متغیرهای عضو نمی‌توانند توسط زیر کلاس‌ها لغو شوند (اگرچه می‌توان به آنها دسترسی داشت).

شما باید یک کلاس را به عنوان open علامت گذاری کنید تا اجازه دهید کلاس فرعی قرار گیرد. به طور مشابه، شما باید ویژگی‌ها و متغیرهای عضو را به‌عنوان open علامت‌گذاری کنید تا آنها را در زیر کلاس لغو کنید. برای جلوگیری از لو رفتن تصادفی جزئیات پیاده سازی به عنوان بخشی از رابط کلاس، کلمه کلیدی open مورد نیاز است.

مرحله 1: کلاس آکواریوم را باز کنید

در این مرحله کلاس Aquarium را open می کنید تا در مرحله بعد بتوانید آن را لغو کنید.

  1. کلاس Aquarium و تمام خصوصیات آن را با کلمه کلیدی open علامت گذاری کنید.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
    open var volume: Int
        get() = width * height * length / 1000
        set(value) {
            height = (value * 1000) / (width * length)
        }
  1. یک ویژگی shape باز با مقدار "rectangle" اضافه کنید.
   open val shape = "rectangle"
  1. یک خاصیت water آزاد را با یک گیرنده اضافه کنید که 90 درصد حجم Aquarium را برمی گرداند.
    open var water: Double = 0.0
        get() = volume * 0.9
  1. کد را به printSize() اضافه کنید تا شکل را چاپ کنید و مقدار آب را به صورت درصدی از حجم چاپ کنید.
fun printSize() {
    println(shape)
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm ")
    // 1 l = 1000 cm^3
    println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
  1. در buildAquarium() کد را تغییر دهید تا یک Aquarium با width = 25 ، length = 25 و height = 40 ایجاد کنید.
fun buildAquarium() {
    val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
    aquarium6.printSize()
}
  1. برنامه خود را اجرا کنید و خروجی جدید را مشاهده کنید.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)

مرحله 2: یک زیر کلاس ایجاد کنید

  1. یک زیر کلاس از Aquarium به نام TowerTank که یک مخزن استوانه ای گرد را به جای مخزن مستطیلی اجرا می کند. می‌توانید TowerTank زیر Aquarium اضافه کنید، زیرا می‌توانید کلاس دیگری را در همان فایل کلاس Aquarium اضافه کنید.
  2. در TowerTank ، ویژگی height را که در سازنده تعریف شده است، لغو کنید. برای لغو یک ویژگی، از کلمه کلیدی override در زیر کلاس استفاده کنید.
  1. ساختار سازنده TowerTank را به diameter برسانید. هنگام فراخوانی سازنده در سوپرکلاس Aquarium از diameter هم برای length و هم برای width استفاده کنید.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
  1. برای محاسبه یک استوانه، خاصیت حجم را نادیده بگیرید. فرمول یک استوانه پی ضربدر شعاع مجذور ارتفاع است. شما باید PI ثابت را از java.lang.Math وارد کنید.
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }
  1. در TowerTank ، ویژگی water را 80 درصد حجم را نادیده بگیرید.
override var water = volume * 0.8
  1. shape "cylinder" را لغو کنید.
override val shape = "cylinder"
  1. کلاس TowerTank نهایی شما باید چیزی شبیه به کد زیر باشد.

Aquarium.kt :

package example.myapp

import java.lang.Math.PI

... // existing Aquarium class

class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }

    override var water = volume * 0.8
    override val shape = "cylinder"
}
  1. در buildAquarium() یک TowerTank با قطر 25 سانتی متر و ارتفاع 45 سانتی متر ایجاد کنید. اندازه را چاپ کنید.

main.kt:

package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium(width = 25, length = 25, height = 40)
    myAquarium.printSize()
    val myTower = TowerTank(diameter = 25, height = 40)
    myTower.printSize()
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)
aquarium initializing
cylinder
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 18 l Water: 14.4 l (80.0% full)

گاهی اوقات می خواهید رفتار یا ویژگی های مشترکی را برای به اشتراک گذاشتن بین برخی کلاس های مرتبط تعریف کنید. Kotlin دو راه برای انجام این کار ارائه می دهد، رابط ها و کلاس های انتزاعی. در این کار، شما یک کلاس AquariumFish انتزاعی برای خواصی که برای همه ماهی ها مشترک است ایجاد می کنید. شما یک رابط به نام FishAction ایجاد می کنید تا رفتار مشترک همه ماهی ها را تعریف کنید.

  • نه یک کلاس انتزاعی و نه یک رابط را نمی توان به تنهایی نمونه سازی کرد، به این معنی که شما نمی توانید اشیایی از آن نوع را مستقیماً ایجاد کنید.
  • کلاس های انتزاعی سازنده دارند.
  • اینترفیس ها نمی توانند منطق سازنده ای داشته باشند یا وضعیتی را ذخیره کنند.

مرحله 1. یک کلاس انتزاعی ایجاد کنید

  1. در قسمت example.myapp ، یک فایل جدید به نام AquariumFish.kt ایجاد کنید.
  2. یک کلاس ایجاد کنید که به آن AquariumFish نیز می‌گویند و آن را با abstract علامت‌گذاری کنید.
  3. یک ویژگی String ، color اضافه کنید و آن را با abstract علامت بزنید.
package example.myapp

abstract class AquariumFish {
    abstract val color: String
}
  1. دو زیر کلاس AquariumFish ، Shark و Plecostomus کنید.
  2. از آنجا که color انتزاعی است، زیر کلاس ها باید آن را پیاده سازی کنند. Shark خاکستری و Plecostomus را طلایی کنید.
class Shark: AquariumFish() {
    override val color = "gray"
}

class Plecostomus: AquariumFish() {
    override val color = "gold"
}
  1. در main.kt ، یک makeFish() برای تست کلاس های خود ایجاد کنید. یک Shark و یک Plecostomus را نمونه برداری کنید، سپس رنگ هر کدام را چاپ کنید.
  2. کد تست قبلی خود را در main() حذف کنید و یک فراخوانی به makeFish() اضافه کنید. کد شما باید چیزی شبیه به کد زیر باشد.

main.kt :

package example.myapp

fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()

    println("Shark: ${shark.color}")
    println("Plecostomus: ${pleco.color}")
}

fun main () {
    makeFish()
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ Shark: gray 
Plecostomus: gold

نمودار زیر نشان دهنده کلاس Shark و کلاس Plecostomus است که زیر کلاس کلاس انتزاعی، AquariumFish است.

A diagram showing the abstract class, AquariumFish, and two subclasses, Shark and Plecostumus.

مرحله 2. یک رابط ایجاد کنید

  1. در AquariumFish.kt ، یک رابط به نام FishAction با متد eat() ایجاد کنید.
interface FishAction  {
    fun eat()
}
  1. FishAction را به هر یک از زیر کلاس‌ها اضافه کنید و eat() را با چاپ کاری که ماهی انجام می‌دهد پیاده‌سازی کنید.
class Shark: AquariumFish(), FishAction {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

class Plecostomus: AquariumFish(), FishAction {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. در تابع makeFish() از هر ماهی که ایجاد کردید با فراخوانی eat() چیزی بخورد.
fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()
    println("Shark: ${shark.color}")
    shark.eat()
    println("Plecostomus: ${pleco.color}")
    pleco.eat()
}
  1. برنامه خود را اجرا کنید و خروجی را مشاهده کنید.
⇒ Shark: gray
hunt and eat fish
Plecostomus: gold
eat algae

نمودار زیر نشان دهنده کلاس Shark و کلاس Plecostomus است که هر دو از رابط FishAction تشکیل شده و پیاده سازی شده اند.

زمان استفاده از کلاس های انتزاعی در مقابل رابط ها

مثال‌های بالا ساده هستند، اما وقتی کلاس‌های مرتبط زیادی دارید، کلاس‌های انتزاعی و رابط‌ها می‌توانند به شما کمک کنند طراحی خود را تمیزتر، سازمان‌دهی‌شده‌تر و نگهداری راحت‌تر داشته باشید.

همانطور که در بالا ذکر شد، کلاس های انتزاعی می توانند سازنده داشته باشند، و رابط ها نمی توانند، اما در غیر این صورت بسیار شبیه هستند. بنابراین، چه زمانی باید از هر کدام استفاده کنید؟

هنگامی که از واسط ها برای نوشتن یک کلاس استفاده می کنید، عملکرد کلاس از طریق نمونه های کلاسی که در آن وجود دارد، گسترش می یابد. ترکیب بندی تمایل دارد استفاده مجدد و استدلال کد را آسان تر از ارث بردن از یک کلاس انتزاعی کند. همچنین، می‌توانید از چندین رابط در یک کلاس استفاده کنید، اما فقط می‌توانید از یک کلاس انتزاعی زیر کلاس‌بندی کنید.

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

  • اگر روش‌های زیادی دارید و یک یا دو پیاده‌سازی پیش‌فرض دارید، برای مثال مانند AquariumAction زیر، از یک رابط استفاده کنید.
interface AquariumAction {
    fun eat()
    fun jump()
    fun clean()
    fun catchFish()
    fun swim()  {
        println("swim")
    }
}
  • هر زمان که نمی توانید کلاس را کامل کنید از یک کلاس انتزاعی استفاده کنید. به عنوان مثال، با بازگشت به کلاس AquariumFish ، می‌توانید تمام AquariumFish را به پیاده‌سازی FishAction ، و یک پیاده‌سازی پیش‌فرض برای eat ارائه دهید در حالی که color را انتزاعی بگذارید، زیرا واقعاً یک رنگ پیش‌فرض برای ماهی وجود ندارد.
interface FishAction  {
    fun eat()
}

abstract class AquariumFish: FishAction {
   abstract val color: String
   override fun eat() = println("yum")
}

کار قبلی کلاس های انتزاعی، رابط ها و ایده ترکیب بندی را معرفی کرد. تفویض رابط یک تکنیک پیشرفته است که در آن متدهای یک رابط توسط یک شی کمک کننده (یا نماینده) پیاده سازی می شود که سپس توسط یک کلاس استفاده می شود. این تکنیک زمانی می‌تواند مفید باشد که از یک رابط در یک سری کلاس‌های غیرمرتبط استفاده می‌کنید: عملکرد رابط مورد نیاز را به یک کلاس کمکی جداگانه اضافه می‌کنید و هر یک از کلاس‌ها از نمونه‌ای از کلاس کمکی برای پیاده‌سازی عملکرد استفاده می‌کنند.

در این کار، از تفویض رابط برای افزودن قابلیت به یک کلاس استفاده می کنید.

مرحله 1: ایجاد یک رابط جدید

  1. در AquariumFish.kt ، کلاس AquariumFish را حذف کنید. به جای ارث بردن از کلاس AquariumFish ، Plecostomus و Shark قرار است رابط هایی را هم برای اکشن ماهی و هم برای رنگ آنها پیاده سازی کنند.
  2. یک رابط جدید به نام FishColor کنید که رنگ را به عنوان یک رشته تعریف می کند.
interface FishColor {
    val color: String
}
  1. Plecostomus را برای پیاده سازی دو رابط، FishAction و FishColor . باید color را از FishColor نادیده بگیرید و eat() را از FishAction .
class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. کلاس Shark خود را تغییر دهید تا به جای ارث بردن از AquariumFish ، دو رابط FishAction و FishColor را نیز پیاده سازی کنید.
class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}
  1. کد تمام شده شما باید چیزی شبیه به این باشد:
package example.myapp

interface FishAction {
    fun eat()
}

interface FishColor {
    val color: String
}

class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}

class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

مرحله 2: یک کلاس تک تن درست کنید

سپس، با ایجاد یک کلاس کمکی که FishColor را پیاده‌سازی می‌کند، تنظیمات مربوط به بخش delegation را پیاده‌سازی می‌کنید. شما یک کلاس پایه به نام GoldColor ایجاد می کنید که FishColor را پیاده سازی می کند - تنها کاری که انجام می دهد این است که رنگ آن طلایی است.

ایجاد چندین نمونه از GoldColor ، زیرا همه آنها دقیقاً یک کار را انجام می دهند. بنابراین Kotlin به شما امکان می دهد کلاسی را اعلام کنید که در آن فقط می توانید یک نمونه از آن را با استفاده از object کلمه کلیدی به جای class ایجاد کنید. کاتلین آن یک نمونه را ایجاد می کند و آن نمونه با نام کلاس ارجاع داده می شود. سپس تمام اشیاء دیگر فقط می توانند از این یک نمونه استفاده کنند—هیچ راهی برای ساختن نمونه های دیگر از این کلاس وجود ندارد. اگر با الگوی سینگلتون آشنا هستید، این روشی است که تک‌تنها را در کاتلین پیاده‌سازی می‌کنید.

  1. در AquariumFish.kt یک شی برای GoldColor ایجاد کنید. رنگ را نادیده بگیرید.
object GoldColor : FishColor {
   override val color = "gold"
}

مرحله 3: اضافه کردن نمایندگی رابط برای FishColor

اکنون برای استفاده از نمایندگی رابط آماده هستید.

  1. در AquariumFish.kt ، رد color از Plecostomus کنید.
  2. کلاس Plecostomus را تغییر دهید تا رنگ آن را از GoldColor دریافت کنید. این کار را با افزودن by GoldColor به اعلان کلاس و ایجاد نمایندگی انجام می دهید. چیزی که می گوید این است که به جای پیاده سازی FishColor ، از پیاده سازی ارائه شده توسط GoldColor استفاده کنید. بنابراین هر بار که color به آن دسترسی پیدا می کند، به GoldColor واگذار می شود.
class Plecostomus:  FishAction, FishColor by GoldColor {
   override fun eat() {
       println("eat algae")
   }
}

با کلاس همانطور که هست، تمام پلکوها طلایی خواهند شد، اما این ماهی ها در واقع رنگ های زیادی دارند. شما می توانید با افزودن یک پارامتر سازنده برای رنگ با GoldColor به عنوان رنگ پیش فرض برای Plecostomus به این موضوع رسیدگی کنید.

  1. کلاس Plecostomus را تغییر دهید تا در fishColor با سازنده آن یک پاس داده شود و پیش فرض آن را GoldColor قرار دهید. تغییر نمایندگی از by GoldColor به by fishColor .
class Plecostomus(fishColor: FishColor = GoldColor):  FishAction,
       FishColor by fishColor {
   override fun eat() {
       println("eat algae")
   }
}

مرحله 4: اضافه کردن نمایندگی رابط برای FishAction

به همین ترتیب، می‌توانید از تفویض رابط برای FishAction استفاده کنید.

  1. در AquariumFish.kt یک کلاس PrintingFishAction بسازید که FishAction را پیاده‌سازی می‌کند، که یک String ، food می‌گیرد، سپس آنچه را که ماهی می‌خورد چاپ می‌کند.
class PrintingFishAction(val food: String) : FishAction {
    override fun eat() {
        println(food)
    }
}
  1. در کلاس Plecostomus ، تابع override eat() را حذف کنید، زیرا آن را با یک نمایندگی جایگزین می‌کنید.
  2. در اعلامیه Plecostomus ، FishAction را به PrintingFishAction واگذار کنید و "eat algae" پاس کنید.
  3. با تمام آن تفویض، هیچ کدی در بدنه کلاس Plecostomus وجود ندارد، بنابراین {} را حذف کنید، زیرا همه موارد لغو توسط تفویض رابط مدیریت می شوند.
class Plecostomus (fishColor: FishColor = GoldColor):
        FishAction by PrintingFishAction("eat algae"),
        FishColor by fishColor

نمودار زیر کلاس‌های Shark و Plecostomus را نشان می‌دهد که هر دو از رابط‌های PrintingFishAction و FishColor ، اما پیاده‌سازی را به آنها محول می‌کنند.

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

یک کلاس داده شبیه یک struct در برخی از زبان‌های دیگر است - این کلاس عمدتاً برای نگهداری برخی داده‌ها وجود دارد - اما یک شی کلاس داده همچنان یک شی است. اشیاء کلاس داده Kotlin دارای مزایای اضافی مانند ابزارهای کاربردی برای چاپ و کپی هستند. در این کار، یک کلاس داده ساده ایجاد می‌کنید و با پشتیبانی Kotlin برای کلاس‌های داده آشنا می‌شوید.

مرحله 1: یک کلاس داده ایجاد کنید

  1. برای نگه داشتن کد جدید، یک decor بسته جدید را در زیر بسته example.myapp اضافه کنید. روی example.myapp در قسمت Project کلیک راست کرده و File > New > Package را انتخاب کنید.
  2. در بسته، یک کلاس جدید به نام Decoration ایجاد کنید.
package example.myapp.decor

class Decoration {
}
  1. برای تبدیل Decoration به یک کلاس داده، پیشوند اعلان کلاس را با کلمه کلیدی data قرار دهید.
  2. یک ویژگی String به نام rocks اضافه کنید تا به کلاس مقداری داده بدهید.
data class Decoration(val rocks: String) {
}
  1. در فایل، خارج از کلاس، یک makeDecorations() برای ایجاد و چاپ نمونه ای از یک Decoration با "granite" اضافه کنید.
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)
}
  1. یک تابع main() برای فراخوانی makeDecorations() اضافه کنید و برنامه خود را اجرا کنید. به خروجی معقولی که ایجاد می شود توجه کنید زیرا این یک کلاس داده است.
⇒ Decoration(rocks=granite)
  1. در makeDecorations() دو شیء Decoration دیگر را که هر دو "تخته" هستند نمونه برداری کنید و آنها را چاپ کنید.
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)

    val decoration2 = Decoration("slate")
    println(decoration2)

    val decoration3 = Decoration("slate")
    println(decoration3)
}
  1. در makeDecorations() یک عبارت print اضافه کنید که نتیجه مقایسه decoration1 1 با decoration2 را چاپ می کند و مورد دوم را که decoration2 3 را با decoration3 مقایسه می کند. از متد ()quals که توسط کلاس های داده ارائه می شود استفاده کنید.
    println (decoration1.equals(decoration2))
    println (decoration3.equals(decoration2))
  1. کد خود را اجرا کنید
⇒ Decoration(rocks=granite)
Decoration(rocks=slate)
Decoration(rocks=slate)
false
true

مرحله 2. از تخریب استفاده کنید

برای به دست آوردن ویژگی های یک شی داده و اختصاص دادن آنها به متغیرها، می توانید آنها را یکی یکی تخصیص دهید، مانند این.

val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver

درعوض، می‌توانید متغیرهایی را برای هر ویژگی یکی بسازید و شی داده را به گروه متغیرها اختصاص دهید. کاتلین مقدار ویژگی را در هر متغیر قرار می دهد.

val (rock, wood, diver) = decoration

به این می گویند ساختارشکنی و مختصر مفیدی است. تعداد متغیرها باید با تعداد ویژگی ها مطابقت داشته باشد و متغیرها به ترتیبی که در کلاس اعلان شده اند نسبت داده می شوند. در اینجا یک مثال کامل است که می توانید در Decoration.kt امتحان کنید.

// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}

fun makeDecorations() {
    val d5 = Decoration2("crystal", "wood", "diver")
    println(d5)

// Assign all properties to variables.
    val (rock, wood, diver) = d5
    println(rock)
    println(wood)
    println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver)
crystal
wood
diver

اگر به یک یا چند ویژگی نیاز ندارید، می‌توانید با استفاده از _ به جای نام متغیر، همانطور که در کد زیر نشان داده شده است، آنها را نادیده بگیرید.

    val (rock, _, diver) = d5

در این کار، با برخی از کلاس های هدف ویژه در کاتلین آشنا می شوید، از جمله موارد زیر:

  • کلاس های تک تن
  • Enums
  • کلاس های مهر و موم شده

مرحله 1: کلاس های تک تن را به یاد بیاورید

مثال قبلی را با کلاس GoldColor به یاد بیاورید.

object GoldColor : FishColor {
   override val color = "gold"
}

از آنجایی که هر نمونه از GoldColor یک کار را انجام می دهد، به جای اینکه به عنوان یک class ، آن را به یک تک تن تبدیل کند، به عنوان یک object اعلان می شود. فقط یک نمونه از آن می تواند وجود داشته باشد.

مرحله 2: یک enum ایجاد کنید

Kotlin همچنین از enums پشتیبانی می کند که به شما امکان می دهد چیزی را برشمارید و با نام آن را ارجاع دهید، دقیقاً مانند سایر زبان ها. با قرار دادن پیشوند اعلان با کلمه کلیدی enum یک enum را اعلام کنید. یک اعلان اولیه فقط به فهرستی از نام ها نیاز دارد، اما می توانید یک یا چند فیلد مرتبط با هر نام را نیز تعریف کنید.

  1. در Decoration.kt ، نمونه ای از enum را امتحان کنید.
enum class Color(val rgb: Int) {
   RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}

Enum ها کمی شبیه تک تون ها هستند - از هر مقدار در شمارش فقط یک و فقط یکی می تواند وجود داشته باشد. برای مثال، فقط می‌تواند یک Color.RED ، یک Color.GREEN و یک Color.BLUE باشد. در این مثال، مقادیر RGB به خاصیت rgb برای نمایش اجزای رنگ اختصاص داده شده است. همچنین می توانید مقدار ترتیبی یک enum را با استفاده از ویژگی ordinal و نام آن را با استفاده از ویژگی name بدست آورید.

  1. نمونه دیگری از enum را امتحان کنید.
enum class Direction(val degrees: Int) {
    NORTH(0), SOUTH(180), EAST(90), WEST(270)
}

fun main() {
    println(Direction.EAST.name)
    println(Direction.EAST.ordinal)
    println(Direction.EAST.degrees)
}
⇒ EAST
2
90

مرحله 3: یک کلاس مهر و موم شده ایجاد کنید

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

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

  1. در AquariumFish.kt ، یک نمونه از کلاس مهر و موم شده را امتحان کنید، با موضوع آبزیان.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()

fun matchSeal(seal: Seal): String {
   return when(seal) {
       is Walrus -> "walrus"
       is SeaLion -> "sea lion"
   }
}

کلاس Seal را نمی توان در فایل دیگری طبقه بندی کرد. اگر می خواهید انواع Seal بیشتری اضافه کنید، باید آنها را در همان فایل اضافه کنید. این باعث می شود کلاس های مهر و موم شده راهی ایمن برای نشان دادن تعداد ثابتی از انواع باشند. به عنوان مثال، کلاس های مهر و موم شده برای بازگشت موفقیت یا خطا از یک API شبکه عالی هستند.

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

کلاس ها و سازنده ها

  • با استفاده از class یک کلاس در Kotlin تعریف کنید.
  • کاتلین به طور خودکار تنظیم کننده ها و دریافت کننده ها را برای ویژگی ها ایجاد می کند.
  • سازنده اولیه را مستقیماً در تعریف کلاس تعریف کنید. مثلا:
    class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
  • اگر سازنده اصلی نیاز به کد اضافی دارد، آن را در یک یا چند بلوک init بنویسید.
  • یک کلاس می تواند یک یا چند سازنده ثانویه را با استفاده از constructor تعریف کند، اما سبک Kotlin به این صورت است که به جای آن از یک تابع کارخانه استفاده می کند.

اصلاح کننده های دید و زیر کلاس ها

  • همه کلاس‌ها و توابع در Kotlin به طور پیش‌فرض public هستند، اما می‌توانید از اصلاح‌کننده‌ها برای تغییر قابلیت مشاهده به internal ، private یا protected استفاده کنید.
  • برای ایجاد یک زیر کلاس، کلاس والد باید open علامت گذاری شود.
  • برای نادیده گرفتن متدها و خصوصیات در یک زیر کلاس، متدها و خصوصیات باید در کلاس والد open علامت گذاری شوند.
  • یک کلاس مهر و موم شده را می توان فقط در همان فایلی که در آن تعریف شده است زیر کلاس قرار داد. با قرار دادن پیشوند اعلان با sealed یک کلاس مهر و موم شده بسازید.

کلاس‌های داده، تک‌تون‌ها و enums

  • با قرار دادن پیشوند اعلان با data ، یک کلاس داده بسازید.
  • Destructuring خلاصه ای برای تخصیص ویژگی های یک شی data به متغیرهای جداگانه است.
  • با استفاده از object به جای class ، یک کلاس singleton بسازید.
  • با استفاده از enum class یک enum تعریف کنید.

کلاس های انتزاعی، رابط ها و تفویض اختیار

  • کلاس‌های انتزاعی و رابط‌ها دو راه برای به اشتراک گذاشتن رفتار مشترک بین کلاس‌ها هستند.
  • یک کلاس انتزاعی خصوصیات و رفتار را تعریف می کند، اما پیاده سازی را به زیر کلاس ها واگذار می کند.
  • یک رابط رفتار را تعریف می کند و ممکن است اجرای پیش فرض را برای برخی یا همه رفتارها ارائه دهد.
  • هنگامی که از واسط ها برای نوشتن یک کلاس استفاده می کنید، عملکرد کلاس از طریق نمونه های کلاسی که در آن وجود دارد، گسترش می یابد.
  • تفویض رابط از ترکیب استفاده می کند، اما پیاده سازی را به کلاس های رابط نیز محول می کند.
  • Composition یک راه قدرتمند برای افزودن قابلیت به یک کلاس با استفاده از تفویض رابط است. به طور کلی ترکیب بندی ترجیح داده می شود، اما ارث بردن از یک کلاس انتزاعی برای برخی از مشکلات مناسب تر است.

مستندات کاتلین

If you want more information on any topic in this course, or if you get stuck, https://kotlinlang.org is your best starting point.

Kotlin tutorials

The https://try.kotlinlang.org website includes rich tutorials called Kotlin Koans, a web-based interpreter , and a complete set of reference documentation with examples.

Udacity course

To view the Udacity course on this topic, see Kotlin Bootcamp for Programmers .

IntelliJ IDEA

Documentation for the IntelliJ IDEA can be found on the JetBrains website.

این بخش، تکالیف احتمالی را برای دانش‌آموزانی که در این آزمایشگاه کد به عنوان بخشی از دوره‌ای که توسط یک مربی هدایت می‌شود، فهرست می‌کند. این وظیفه مربی است که موارد زیر را انجام دهد:

  • در صورت نیاز تکالیف را تعیین کنید.
  • نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
  • تکالیف را نمره دهید.

مربیان می‌توانند از این پیشنهادات به اندازه‌ای که می‌خواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر می‌کنند مناسب است به آنها اختصاص دهند.

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

یه این سوالات پاسخ دهید

سوال 1

Classes have a special method that serves as a blueprint for creating objects of that class. What is the method called?

▢ A builder

▢ An instantiator

▢ A constructor

▢ A blueprint

سوال 2

Which of the following statements about interfaces and abstract classes is NOT correct?

▢ Abstract classes can have constructors.

▢ Interfaces can't have constructors.

▢ Interfaces and abstract classes can be instantiated directly.

▢ Abstract properties must be implemented by subclasses of the abstract class.

سوال 3

Which of the following is NOT a Kotlin visibility modifier for properties, methods, etc.?

internal

nosubclass

protected

private

Question 4

Consider this data class:
data class Fish(val name: String, val species:String, val colors:String)
Which of the following is NOT valid code to create and destructure a Fish object?

val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")

val (name2, _, colors2) = Fish("Bitey", "shark", "gray")

val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")

val (name4, species4, colors4) = Fish("Harry", "halibut")

Question 5

Let's say you own a zoo with lots of animals that all need to be taken care of. Which of the following would NOT be part of implementing caretaking?

▢ An interface for different types of foods animals eat.

▢ An abstract Caretaker class from which you can create different types of caretakers.

▢ An interface for giving clean water to an animal.

▢ A data class for an entry in a feeding schedule.

Proceed to the next lesson: 5.1 Extensions

For an overview of the course, including links to other codelabs, see "Kotlin Bootcamp for Programmers: Welcome to the course."