زبان برنامه نویسی کاتلین را یاد بگیرید

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

اعلام متغیر

کاتلین از دو کلمه کلیدی مختلف برای اعلام متغیرها استفاده می کند: val و var .

  • val برای متغیری استفاده کنید که مقدار آن هرگز تغییر نمی کند. شما نمی توانید مقداری را به متغیری که با استفاده از val اعلان شده است دوباره اختصاص دهید.
  • برای متغیری که مقدار آن می تواند تغییر کند از var استفاده کنید.

در مثال زیر، count یک متغیر از نوع Int است که مقدار اولیه 10 به آن اختصاص داده شده است:

var count: Int = 10

Int نوعی است که یک عدد صحیح را نشان می دهد، یکی از انواع عددی متعددی که می توان در Kotlin نمایش داد. مشابه سایر زبان‌ها، می‌توانید بسته به داده‌های عددی خود از Byte ، Short ، Long ، Float و Double نیز استفاده کنید.

کلمه کلیدی var به این معنی است که شما می توانید مقادیر را برای count مجدد در صورت نیاز اختصاص دهید. به عنوان مثال، می توانید مقدار count را از 10 به 15 تغییر دهید:

var count: Int = 10
count = 15

با این حال، برخی از مقادیر قرار نیست تغییر کنند. String به نام languageName در نظر بگیرید. اگر می خواهید اطمینان حاصل کنید که languageName همیشه مقدار "Kotlin" را در خود دارد، می توانید languageName با استفاده از کلمه کلیدی val اعلام کنید:

val languageName: String = "Kotlin"

این کلمات کلیدی به شما این امکان را می‌دهند که در مورد آنچه می‌توان تغییر داد به صراحت صحبت کنید. در صورت نیاز از آنها به نفع خود استفاده کنید. اگر یک مرجع متغیر باید قابل تخصیص مجدد باشد، آن را به عنوان var اعلام کنید. در غیر این صورت از val استفاده کنید.

استنتاج تایپ کنید

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

از آنجایی که مقدار "Kotlin" از نوع String است، کامپایلر استنباط می کند که languageName نیز یک String است. توجه داشته باشید که کاتلین یک زبان استاتیکی است. این بدان معنی است که نوع در زمان کامپایل حل می شود و هرگز تغییر نمی کند.

در مثال زیر، languageName به عنوان یک String استنباط شده است، بنابراین نمی‌توانید تابع‌هایی را که بخشی از کلاس String نیستند فراخوانی کنید:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase() تابعی است که فقط روی متغیرهایی از نوع String فراخوانی می شود. از آنجایی که کامپایلر Kotlin languageName به عنوان یک String استنباط کرده است، می توانید با خیال راحت toUpperCase() را فراخوانی کنید. با این حال، inc() یک تابع عملگر Int است، بنابراین نمی توان آن را روی یک String فراخوانی کرد. رویکرد کاتلین برای استنتاج نوع به شما هم مختصر و هم ایمنی نوع می دهد.

ایمنی پوچ

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

// Fails to compile
val languageName: String = null

برای اینکه یک متغیر مقدار تهی داشته باشد، باید از نوع nullable باشد. شما می توانید با پسوند نوع آن با ? ، همانطور که در مثال زیر نشان داده شده است:

val languageName: String? = null

با String? تایپ کنید، می توانید یک مقدار String یا null به languageName اختصاص دهید.

شما باید متغیرهای nullable را با دقت مدیریت کنید یا در معرض خطر NullPointerException مخوف باشید. به عنوان مثال، در جاوا، اگر بخواهید متدی را بر روی یک مقدار تهی فراخوانی کنید، برنامه شما از کار می افتد.

کاتلین تعدادی مکانیسم برای کار ایمن با متغیرهای پوچ ارائه می کند. برای اطلاعات بیشتر، الگوهای رایج کاتلین را در اندروید ببینید: پوچ‌پذیری .

شرایط

کاتلین چندین مکانیسم برای اجرای منطق شرطی دارد. رایج ترین آنها عبارت if-else است. اگر عبارتی که در پرانتز در کنار یک کلمه کلیدی if قرار گرفته باشد، به true ارزیابی شود، کد درون آن شاخه (یعنی کد بلافاصله پس از آن که در پرانتز پیچیده شده است) اجرا می شود. در غیر این صورت کد داخل شاخه else اجرا می شود.

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

می توانید چندین شرط را با استفاده else if نشان دهید. همانطور که در مثال زیر نشان داده شده است، این به شما امکان می‌دهد منطق ریزتر و پیچیده‌تری را در یک دستور شرطی نشان دهید:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

عبارات شرطی برای نشان دادن منطق حالتی مفید هستند، اما ممکن است متوجه شوید که هنگام نوشتن آنها، خود را تکرار می کنید. در مثال بالا، شما به سادگی یک String را در هر شاخه چاپ می کنید. برای جلوگیری از این تکرار، کاتلین عبارات شرطی را ارائه می دهد. نمونه آخر را می توان به صورت زیر بازنویسی کرد:

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

به طور ضمنی، هر شاخه شرطی نتیجه عبارت را در خط نهایی خود برمی گرداند، بنابراین نیازی به استفاده از کلمه کلیدی return ندارید. چون نتیجه هر سه شاخه از نوع String است، نتیجه عبارت if-else نیز از نوع String است. در این مثال، answerString یک مقدار اولیه از نتیجه عبارت if-else اختصاص داده شده است. استنتاج نوع را می توان برای حذف اعلان نوع صریح برای answerString استفاده کرد، اما اغلب ایده خوبی است که آن را برای وضوح اضافه کنید.

همانطور که در مثال زیر نشان داده شده است، همانطور که پیچیدگی دستور شرطی شما افزایش می یابد، ممکن است در نظر داشته باشید که عبارت if-else خود را با عبارت When جایگزین کنید:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

هر شاخه در عبارت when با یک شرط، یک فلش ( -> ) و یک نتیجه نشان داده می شود. اگر شرط سمت چپ فلش درست باشد، نتیجه عبارت سمت راست برگردانده می شود. توجه داشته باشید که اجرا از یک شاخه به شاخه دیگر نمی افتد. کد در مثال عبارت when از نظر عملکردی با مثال قبلی برابر است اما احتمالاً خواندن آن آسان‌تر است.

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

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

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

توابع

می توانید یک یا چند عبارت را در یک تابع گروه بندی کنید. به جای تکرار همان سری عبارات هر بار که به نتیجه نیاز دارید، می توانید عبارات را در یک تابع بپیچید و به جای آن تابع را فراخوانی کنید.

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

با تکیه بر نمونه های قبلی، در اینجا یک تابع کامل Kotlin آمده است:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

تابع موجود در مثال بالا نام generateAnswerString دارد. هیچ ورودی نمی گیرد. خروجی آن از نوع String است. برای فراخوانی یک تابع، از نام آن و به دنبال آن عملگر فراخوانی ( () ) استفاده کنید. در مثال زیر، متغیر answerString با نتیجه generateAnswerString() مقداردهی اولیه می شود.

val answerString = generateAnswerString()

همانطور که در مثال زیر نشان داده شده است، توابع می توانند آرگومان ها را به عنوان ورودی دریافت کنند:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

هنگام اعلان یک تابع، می توانید هر تعداد آرگومان و انواع آنها را مشخص کنید. در مثال بالا، generateAnswerString() یک آرگومان به نام countThreshold از نوع Int می گیرد. در داخل تابع، می توانید با استفاده از نام آرگومان به آن مراجعه کنید.

هنگام فراخوانی این تابع، باید یک آرگومان در داخل پرانتز فراخوانی تابع قرار دهید:

val answerString = generateAnswerString(42)

ساده کردن اعلان های تابع

generateAnswerString() یک تابع نسبتا ساده است. تابع یک متغیر را اعلام می کند و سپس بلافاصله برمی گردد. هنگامی که نتیجه یک عبارت واحد از یک تابع برگردانده می شود، می توانید با بازگرداندن مستقیم نتیجه عبارت if-else موجود در تابع، از اعلام یک متغیر محلی صرفنظر کنید، همانطور که در مثال زیر نشان داده شده است:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

همچنین می توانید کلمه کلیدی بازگشت را با عملگر انتساب جایگزین کنید:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

توابع ناشناس

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

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

مانند توابع نامگذاری شده، توابع ناشناس می توانند شامل هر تعداد عبارت باشند. مقدار بازگشتی تابع نتیجه عبارت نهایی است.

در مثال بالا، stringLengthFunc حاوی یک مرجع به یک تابع ناشناس است که یک String به عنوان ورودی می گیرد و طول String ورودی را به عنوان خروجی از نوع Int برمی گرداند. به همین دلیل، نوع تابع به صورت (String) -> Int نشان داده می شود. با این حال، این کد تابع را فراخوانی نمی کند. برای بازیابی نتیجه تابع، باید آن را مانند تابعی با نام فراخوانی کنید. همانطور که در مثال زیر نشان داده شده است، هنگام فراخوانی stringLengthFunc باید یک String ارائه کنید:

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

val stringLength: Int = stringLengthFunc("Android")

توابع مرتبه بالاتر

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

در اینجا مثالی از یک تابع مرتبه بالاتر آورده شده است:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

تابع stringMapper() یک String به همراه تابعی می گیرد که مقدار Int را از String که شما به آن ارسال می کنید مشتق می کند.

همانطور که در مثال زیر نشان داده شده است String می‌توانید stringMapper() را با ارسال یک String و تابعی که پارامتر ورودی دیگر را برآورده می‌کند Int فراخوانی کنید.

stringMapper("Android", { input ->
    input.length
})

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

stringMapper("Android") { input ->
    input.length
}

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

کلاس ها

تمام انواع ذکر شده تا کنون در زبان برنامه نویسی کاتلین ساخته شده اند. اگر می خواهید نوع سفارشی خود را اضافه کنید، می توانید با استفاده از کلمه کلیدی class یک کلاس تعریف کنید، همانطور که در مثال زیر نشان داده شده است:

class Car

خواص

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

class Car {
    val wheels = listOf<Wheel>()
}

توجه داشته باشید که wheels یک public val است، به این معنی که wheels از خارج از کلاس Car قابل دسترسی هستند و نمی‌توان آن را مجدداً اختصاص داد. اگر می خواهید نمونه ای از Car را بدست آورید، ابتدا باید سازنده آن را فراخوانی کنید. از آنجا می توانید به هر یک از ویژگی های قابل دسترسی آن دسترسی داشته باشید.

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

اگر می‌خواهید چرخ‌های خود را سفارشی کنید، می‌توانید یک سازنده سفارشی تعریف کنید که مشخص می‌کند خصوصیات کلاس شما چگونه مقداردهی اولیه می‌شوند:

class Car(val wheels: List<Wheel>)

در مثال بالا، سازنده کلاس یک List<Wheel> به عنوان آرگومان سازنده می گیرد و از آن آرگومان برای مقداردهی اولیه ویژگی wheels خود استفاده می کند.

توابع کلاس و کپسولاسیون

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

در مثال زیر، ویژگی doorLock از هر چیزی خارج از کلاس Car خصوصی نگه داشته می شود. برای باز کردن قفل ماشین، باید تابع unlockDoor() را که در یک کلید معتبر عبور می کند، فراخوانی کنید، همانطور که در مثال زیر نشان داده شده است:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

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

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

با ترکیبی از ویژگی ها و توابع، می توانید کلاس هایی ایجاد کنید که انواع اشیاء را مدل می کنند.

قابلیت همکاری

یکی از مهم ترین ویژگی های کاتلین، قابلیت همکاری سیال آن با جاوا است. از آنجا که کد Kotlin به بایت کد JVM کامپایل می شود، کد Kotlin شما می تواند مستقیماً به کد جاوا و بالعکس فراخوانی کند. این بدان معناست که شما می توانید کتابخانه های جاوا موجود را مستقیماً از Kotlin استفاده کنید. علاوه بر این، اکثر API های اندروید به زبان جاوا نوشته شده اند و می توانید مستقیماً از Kotlin با آنها تماس بگیرید.

مراحل بعدی

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