آموزش کاتلین (Kotlin) — جامع و رایگان | از صفر تا صد

۲۹۶۲ بازدید
آخرین به‌روزرسانی: ۲۱ خرداد ۱۴۰۳
زمان مطالعه: ۸۱ دقیقه
آموزش کاتلین (Kotlin) — جامع و رایگان | از صفر تا صد

کاتلین یک زبان نسبتاً جدید برنامه‌نویسی است که از سوی JetBrains برای اپلیکیشن‌های مدرن چندپلتفرمی توسعه یافته است. امروزه کاتلین کاربرد گسترده‌ای برای توسعه اندروید دارد و جایگزین جاوا شده است. دلیل این امر آن است که زبان کاتلین امن و منسجم است و خواندن و نوشتن کد کاتلین آسان است. در این مقاله جامع به صورت تفصیلی به آموزش کاتلین خواهیم پرداخت.

فهرست مطالب این نوشته
997696

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

زبان کاتلین تحت لایسنس آپاچی نسخه 2.0 توزیع یافته است. کامپایلر این زبان، پلاگین IntelliJ IDEA و همچنین بهینه‌سازی‌های صورت گرفته روی کتابخانه‌های ابتدایی جاوا و ابزارهای Build همگی متن-باز هستند.

کاتلین جایگزین جاوا و اندروید می‌شود: زبان برنامه‌نویسی کاتلین 100% امکان جایگزینی جاوا و اندروید را دارد. این بدان معنی است که همه کد‌های کنونی جاوا/اندروید شما می‌توانند به صورت کامل به زبان کاتلین نوشته شوند.

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

یادگیری کاتلین آسان است: اگر از قبل با زبان‌های برنامه‌نویسی دیگر مانند جاوا، اسکالا، گرووی، سی شارپ، جاوا اسکریپت و Gosu آشنا باشید، یادگیری کاتلین برای شما آسان خواهد بود.

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

کاتلین امن است: کاتلین یک زبان نوع‌بندی استاتیک است. از این رو بررسی نوع در زمان کامپایل و نه زمان اجرا انجام می‌گیرد و باگ‌های ساده در همین مرحله به دام می‌افتند.

زبان برنامه‌نویسی کاتلین می‌تواند روی ماشین مجازی جاوا (JVM) اجرا شود. این زبان ترکیبی از پارادایم‌های برنامه‌نویسی شیءگرا و تابعی را در یک پلتفرم نامحدود، خودکفا و متمایز ترکیب کرده است.

تاریخچه زبان برنامه نویسی کاتلین

  • در سال 2016 نخستین نسخه کاتلین یعنی Kotlin v1.0 عرضه شد. در سال 2017 گوگل اعلام کرد که در زمینه اندروید پشتیبانی دست اولی از زبان کاتلین به عمل می‌آورد.
  • در سال 2018 کاتلین نسخه 1.2 به همراه امکان توزیع کد بین JVM و جاوا اسکریپت عرضه شد.
  • در سال 2019 گوگل اعلام کرد که کاتلین زبان برنامه‌نویسی ترجیحی این شرکت برای توسعه اپلیکیشن‌های اندرویدی محسوب می‌شود.

تنظیم محیط توسعه کاتلین

در این بخش مراحل راه‌اندازی محیط کاری را با نصب کاتلین توضیح می‌دهیم.

نصب جاوا

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

نصب IDE-های جاوا

خوشبختانه IDE-های متعددی برای جاوا وجود دارند. به منظور توسعه کد کاتلین می‌توانید از یکی از IDE-های Eclipse ،NetBeans یا IntelliJ IDEA استفاده کنید. ما در این راهنما از ایکلیپس استفاده می‌کنیم.

نصب کاتلین

برای نصب ایکلیپس در کاتلین باید به بخش Help ایکلیپس بروید و روی گزینه Eclipse Marketplace کلیک کنید.

آموزش کاتلین

اکنون کلیدواژه Kotlin را در کار جستجو وارد کنید. با کلیک روی کلمه Go لیستی از پلاگین‌ها نمایان می‌شود. در این بخش باید یک پلاگین برای کاتلین ببینید. با کلیک روی این لینک، پلاگین کاتلین برای ایکلیپس را نصب کنید.

آموزش کاتلین

IDE ایکلیپس را ری‌استارت کنید، زمانی که نصب کامل شد، می‌توانید یک آیکون میانبر در گوشه راست-بالای IDE ایکلیپس ببینید. این یک روش دسترسی سریع است.

آموزش کاتلین

روش دیگر برای دسترسی به کاتلین در IDE ایکلیپس این است که به منوی Windows> Perspectives>Open Perspectives بروید و گزینه Others را انتخاب کنید. در این بخش می‌توانید فهرستی از پلاگین‌های نصب شده اخیر را مانند تصویر زیر ببینید:

آموزش کاتلین

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

نوشتن نخستین برنامه کاتلین

کار خود را با نخستین پروژه کاتلین آغاز می‌کنیم. به این منظور از منوی File گزینه New را انتخاب کرده و سپس با انتخاب Others یک پروژه کاتلین از فهرست ارائه شده ایجاد کنید.

آموزش کاتلین

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

آموزش کاتلین

چنان که دیدید، با طی این مراحل ساده و دانلود نصب ایکلیپس و پلاگین کاتلین روی سیستم می‌توانید اقدام به برنامه‌نویسی کاتلین بکنید.

برنامه Hello, World

برنامه «!Hello, World» به یک برنامه ساده گفت می‌شود که خروجی !Hello, World را روی صفحه نمایش می‌دهد. از آنجا که این یک برنامه ساده است، غالباً برای معرفی یک زبان جدید برنامه‌نویسی مورد استفاده قرار می‌گیرد. در این بخش با نوشتن یک برنامه !Hello, World با ساختار و چارچوب زبان کاتلین آشنا می‌شویم:

1// Hello World Program
2
3fun main(args : Array<String>) {
4    println("Hello, World!")
5}

زمانی که برنامه فوق را اجرا کنید، خروجی زیر را مشاهده می‌کنید:

Hello, World!

طرز کار برنامه !Hello, World با زبان برنامه‌نویسی کاتلین چگونه است؟

کد موجود در خط نخست این برنامه به صورت زیر است:

// Hello World Program

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

خط دوم برنامه ما به این صورت است:

fun main(args: Array<String>) { ... }

این تابع اصلی (main) برنامه است که وجود آن در هر اپلیکیشن کاتلین ضروری است. کامپایلر کاتلین اجرای کد را از تابع main آغاز می‌کند.

این تابع یک آرایه از رشته‌ها به عنوان پارامتر می‌گیرد و یک Unit بازگشت می‌دهد. در مورد تابع‌ها و پارامتر‌های آن در کاتلین در بخش‌های بعدی بیشتر توضیح خواهیم داد.

فعلاً به خاطر داشته باشید که تابع main یک تابع الزامی است که نقطه ورودی هر برنامه کاتلین محسوب می‌شود. امضای تابع main به صورت زیر است:

1fun main(args : Array<String>) {
2    ... .. ...
3}

خط سوم برنامه به صورت زیر است:

println("Hello, World!")

تابع ()println پیام مورد نظر را درون علامت گیومه پرینت کرده و یک کاراکتر newline به استریم خروجی استاندارد اضافه می‌کند. در این برنامه عبارت !Hello, World و یک خط جدید در خروجی چاپ می‌شود.

مقایسه با برنامه Hello, World در جاوا

چنان که قبلاً اشاره کردیم، کاتلین به صورت 100% قابلیت جایگزین کردن جاوا را دارد. معادل برنامه Hello, World در زبان جاوا به صورت زیر است:

1// Hello World Program
2
3class HelloWorldKt {
4    public static void main(String[] args) {
5        System.out.println("Hello, World!"); 
6    }
7}

چند نکته مهم

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

اگر از IntelliJ IDEA استفاده می‌کنید، با مراجعه به منوی Run > Edit Configurations می‌توانید این کلاس را ببینید. اگر نام فایل کاتلین به صورت HelloWorld.kt باشد، کامپایلر کلاس HelloWorldKt را برای شما ایجاد می‌کند.

آموزش کاتلین

تابع ()println به صورت داخلی ()System.out.println را فراخوانی می‌کند.

اگر از IntelliJ IDEA استفاده می‌کنید، کرسر ماوس خود را در کنار عبارت println قرار دهید و به منوی Navigate > Declaration بروید. همچنین می‌توانید کلیدهای ترکیبی Ctrl+B (در مک: Cmd+B) را بزنید. به این ترتیب فایل اعلان Console.kt باز می‌شود. در این فایل می‌توان دید که تابع ()println به صورت داخلی ()System.out.println را فراخوانی‌ می‌کند.

آموزش کاتلین

متغیرهای کاتلین و انواع ابتدایی

چنان که می‌دانید، متغیر در یک برنامه به مکانی از حافظه گفته می‌شود که داده‌ها را در خود نگهداری می‌کند. برای مشخص ساختن این ناحیه ذخیره‌سازی، هر متغیر باید یک نام یکتا (شناسه) داشته باشد.

شیوه اعلان متغیر در کاتلین

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

1var language = "French"
2val score = 95

اختلاف بین var و val در ادامه این بخش توضیح داده شده است. فعلاً روی شیوه اعلان متغیر تمرکز می‌کنیم. در کد فوق language یک متغیر از نوع String و Score نیز متغیری با نوع Int است. در کاتلین لزومی به اعلام صریح نوع متغیر وجود ندارد و این زبان خودش ‌می‌تواند نوع متغیر را برای شما مشخص بکند. کامپایلر خودش تشخیص می‌دهد که "French" یک رشته (String) و 95 یک عدد صحیح (Int) است. این قابلیت در زبان‌های برنامه‌نویسی به نام «استنباط نوع» (type inference) شناخته می‌شود.

با این حال، در صورت تمایل می‌توانید نوع یک متغیر را به صورت صریح نیز تعیین کنید:

1var language: String = "French"
2val score: Int = 95

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

1var language: String      // variable declaration of type String
2... .. ...
3language = "French"       // variable initialization
4
5val score: Int          // variable declaration of type Int 
6... .. ...
7score = 95             // variable initialization

اما مثال زیر موجب بروز خطا می‌شود:

1var language// Error
2language = "French"

در مثال فوق، نوع متغیر language به صورت صریحی مشخص نشده است. همچنین متغیر نیز درون گزاره اعلان، مقداردهی نشده است.

1var language: String
2language = 14// Error

در مثال فوق نیز تلاش کرده‌ایم که مقدار 14 (عدد صحیح) را به متغیری از نوع متفاوت (String) نسبت دهیم که موجب بروز خطا شده است.

تفاوت بین var و val

  • Val (ارجاع تغییرناپذیر) – متغیری که با استفاده از کلیدواژه val اعلان شود، پس از این که مقداری به آن انتساب یافت، دیگر نمی‌تواند تغییر داده شود. این وضعیت شبیه متغیر final در جاوا است.
  • Var (ارجاع تغییرپذیر) – متغیری که با کلیدواژه var اعلان شود، می‌تواند در ادامه برنامه مقدار متفاوتی بگیرد. این کلیدواژه معادل متغیرهای معمولی جاوا است.

به مثال‌های زیر توجه کنید:

1var language = "French"
2language = "German"

در مثال فوق متغیر language پس از مقدار‌دهی اولیه، در ادامه مقدار German را گرفته است. از آنجا که این متغیر با استفاده از کلیدواژه var اعلان یافته است، این کد به درستی کار می‌کند.

1val language = "French"
2language = "German"// Error

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

انواع ابتدایی کاتلین

کاتلین یک زبان با نوع‌بندی استاتیک مانند جاوا است. معنی این حرف آن است که نوع یک متغیر در زمان کامپایل مشخص می‌شود. به مثال زیر توجه کنید:

1val language: Int
2val marks = 12.3

در مثال فوق، کامپایلر پیش از کامپایل کردن کد می‌داند که language دارای نوع Int است و marks از نوع داده Double است.

انواع داخلی داده‌ها در زبان کاتلین به صورت زیر دسته‌بندی می‌شوند:

  • اعداد
  • کاراکترها
  • مقادیر بولی
  • آرایه‌ها

نوع عددی

اعداد در کاتلین انواعی مشابه زبان جاوا دارند. شش نوع داده داخلی در کاتلین وجود دارد که اعداد را نمایش می‌دهند:

  • Byte
  • Short
  • Int
  • Long
  • Float
  • Double

نوع Byte

نوع داده byte مقادیری درباره 128- تا 127 می‌گیرد. این نوع معادل عدد صحیح مکمل دوی هشت بیتی علامت‌دار است. از این نوع داده در مواردی که عدد در بازه 128 -تا 127 قرار می‌گیرد، به جای نوع Int یا دیگر انواع داده صحیح برای صرفه‌جویی در مصرف حافظه استفاده می‌شود.

مثال

1fun main(args : Array<String>) {
2    val range: Byte = 112
3    println("$range")
4
5    // The code below gives error. Why?
6    // val range1: Byte = 200
7}

زمانی که برنامه فوق اجرا شود، خروجی زیر چاپ می‌شود:

112

نوع Short

نوع داده Short می‌تواند مقادیری بین 32678- تا 32677 داشته باشد که معادل عدد صحیح مکمل دوی 16 بیتی علامت‌دار است. در صورتی که مطمئن هستید مقدار یک متغیر در بازه [32767, 32768-] قرار دارد، می‌توانید از این نوع داده به جای انواع داده دیگر استفاده کنید.

مثال

1fun main(args : Array<String>) {
2
3    val temperature: Short = -11245
4    println("$temperature")
5}

زمانی که برنامه را اجرا کنید، خروجی زیر تولید می‌شود:

-11245

نوع Int

نوع داده Int مقادیری بین 231-2^{31} تا 23112^{31}-1 می‌گیرد که معادل عدد صحیح مکمل دوی 2 بیتی علامت‌دار است.

مثال

1fun main(args : Array<String>) {
2
3    val score: Int =  100000
4    println("$score")
5}

زمانی که برنامه را اجرا کنید، خروجی به صورت زیر خواهد بود:

100000

اگر یک عدد صحیحی را بدون انتساب صریح نوع، بین 231-2^{31} تا 23112^{31}-1 به یک متغیر انتساب دهید، این متغیر از نوع Int خواهد بود. به مثال زیر توجه کنید:

1fun main(args : Array<String>) {
2
3   // score is of type Int
4    val score = 10
5    println("$score")
6}

اگر از IntelliJ IDEA استفاده می‌کنید، می‌توانید کرسر را روی متغیر قرار داده و با فشردن کلیدهای ترکیبی Ctrl+Shift+P نوع آن را ببینید.

آموزش کاتلین

نوع Long

نوع داده Long مقادیری بین 263-2^{63} تا 26312^{63}-1 می‌گیرد که معادل عدد صحیح مکمل دوی 63 بیتی علامت‌دار است.

مثال

fun main(args: Array<String>)

1fun main(args : Array<String>) {
2
3    val highestScore: Long = 9999
4    println("$highestScore")
5}

زمانی که برنامه فوق را اجرا کنید، با خروجی زیر مواجه می‌شوید:

9999

اگر بدون تعیین صریح نوع یک متغیر مقداری بزرگ‌تر از 231-2^{31} تا 23112^{31}-1 به یک متغیر بدهید، این متغیر به صورت خودکار به نوع Long تبدیل می‌شود. به مثال زیر توجه کنید:

1val distance = 10000000000 // distance variable of type Long

به طور مشابه می‌توانید از حرف بزرگ L به صورت زیر برای تعیین نوع یک متغیر به صورت Long استفاده کنید:

1val distance = 100L// distance value of type Long

نوع Double

نوع داده Double دارای دقت دو برابر اعشار 64 بیتی است.

مثال

1fun main(args : Array<String>) {
2
3    // distance is of type Double
4    val distance = 999.5
5    println("$distance")
6}

زمانی که این برنامه را اجرا کنید، خروجی زیر تولید می‌شود:

999.5

نوع Float

نوع داده Float یک عدد اعشاری 32 بیتی با دقت منفرد است.

مثال

1fun main(args : Array<String>) {
2
3    // distance is of type Float
4    val distance = 19.5F
5    println("$distance")
6}

خروجی برنامه فوق به صورت زیر است:

19.5

توجه کنید که ما به جای 19.5‌ مقدار 19.5F را در برنامه فوق داریم. دلیل این امر آن است که 19.5 یک مقدار لفظی Double است و نمی‌توان مقدار Double را به متغیر Float انتساب داد. برای این که به کامپایلر اعلام کنیم با عدد 19.5 به صورت Float برخورد کند، باید از حرف بزرگ F در انتهای آن استفاده کنیم.

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

1fun main(args : Array<String>) {
2
3    var test: Number = 12.2
4    println("$test")
5
6    test = 12
7    // Int smart cast from Number
8    println("$test")
9
10    test = 120L
11    // Long smart cast from Number
12    println("$test")
13}

خروجی برنامه فوق به صورت زیر است:

12.2
12
120

نوع Char

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

1fun main(args : Array<String>) {
2
3    val letter: Char
4    letter = 'k'
5    println("$letter")
6}

خروجی برنامه فوق به صورت زیر است:

k

در جاوا می‌توان مانند زیر عمل کرد:

1char letter = 65;

با این حال کد زیر در کاتلین موجب تولید خطا می‌شود:

1var letter: Char = 65 // Error

نوع Boolean

نوع داده Boolean دو مقدار به صورت True و False می‌تواند بگیرد.

مثال

1fun main(args : Array<String>) {
2
3    val flag = true
4    println("$flag")
5}

مقادیر بولی در گزاره‌های تصمیم‌گیری استفاده می‌شوند.

آرایه‌های کاتلین

یک آرایه در کاتلین داده‌ها (مقادیر) از یک نوع نگه‌داری می‌کند. برای نمونه می‌توانید یک آرایه ایجاد کنید که 100 مقدار از نوع Int را در خود ذخیره کند. آرایه‌ها در کاتلین به وسیله کلاس Array نمایش می‌یابند. این کلاس دارای تابع‌های get و set، مشخصه size و چند تابع عضو مفید دیگر است.

رشته‌های کاتلین

رشته‌ها در کاتلین به وسیله کلاس String نمایش می‌یابند. لفظ‌های رشته‌ای مانند "this is a string" به وسیله یک وهله از این کلاس پیاده‌سازی می‌شوند.

عملگرهای کاتلین

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

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

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

عملگرتوضیح
+جمع (برای الحاق دو رشته نیز استفاده می‌شود.)
-عملگر تفریق
*عملگر ضرب
/عملگر تقسیم
%عملگر باقیمانده

مثال عملگرهای حسابی در کاتلین

1fun main(args: Array<String>) {
2
3    val number1 = 12.5
4    val number2 = 3.5
5    var result: Double
6
7    result = number1 + number2
8    println("number1 + number2 = $result")
9
10    result = number1 - number2
11    println("number1 - number2 = $result")
12
13    result = number1 * number2
14    println("number1 * number2 = $result")
15
16    result = number1 / number2
17    println("number1 / number2 = $result")
18
19    result = number1 % number2
20    println("number1 % number2 = $result")
21}

خروجی برنامه فوق چنین است:

number1 + number2 = 16.0
number1 - number2 = 9.0
number1 * number2 = 43.75
number1 / number2 = 3.5714285714285716
number1% number2 = 2.0

عملگر + برای الحاق مقادیر string استفاده می‌شود.

مثالی از الحاق رشته‌ها

1fun main(args: Array<String>) {
2
3    val start = "Talk is cheap. "
4    val middle = "Show me the code. "
5    val end = "- Linus Torvalds"
6
7    val result = start + middle + end
8    println(result)
9}

خروجی برنامه فوق به صورت زیر است:

Talk is cheap. Show me the code. - Linus Torvalds

طرز کار عملگرهای حسابی چگونه است؟

فرض کنید از عملگر + برای جمع زدن دو عدد a و b استفاده می‌کنیم. در پشت صحنه a + b تابع عضو a.plus(b) را فراخوانی می‌کند. عملگر plus طوری overload شده که با مقادیر String و دیگر انواع داده مقدماتی (به جز Char و Boolean) نیز کار کند.

// + operator for basic types
operator fun plus(other: Byte): Int
operator fun plus(other: Short): Int
operator fun plus(other: Int): Int
operator fun plus(other: Long): Long
operator fun plus(other: Float): Float
operator fun plus(other: Double): Double

// for string concatenation
operator fun String?.plus(other: Any?): String

همچنین می‌توانید از عملگر + و از طریق overload کردن تابع ()plus برای کار با انواع تعریف شده از سوی کاربر (مانند شیءها) استفاده کنید. در جدول زیر عملگرهای حسابی و تابع‌های متناظر آن‌ها ارائه شده است:

عبارتنام تابعترجمه
a + bplusa.plus(b)
a - bminusa.minus(b)
a * btimesa.times(b)
a / bdiva.div(b)
a % bmoda.mod(b)

عملگرهای انتساب

عملگرهای انتساب برای نسبت دادن یک مقدار به متغیر استفاده می‌شوند. ما قبلاً نمونه‌ای از کاربرد عملگر انتساب ساده (=) را دیده‌ایم.

val age = 5

در مقال فوق مقدار 5 با استفاده از عملگر = به متغیر age انتساب می‌یابد. در ادامه فهرستی از همه عملگرهای انتساب و تابع‌های متناظر آن‌ها را می‌بینید:

عبارتمعادلترجمه
a +=ba = a + ba.plusAssign(b)
a -= ba = a - ba.minusAssign(b)
a *= ba = a * ba.timesAssign(b)
a /= ba = a / ba.divAssign(b)
a %= ba = a % ba.modAssign(b)

مثالی از عملگر‌های انتساب

1fun main(args: Array<String>) {
2    var number = 12
3
4    number *= 5   // number = number*5
5    println("number  = $number")
6}

با اجرای کد فوق خروجی زیر به دست می‌آید:

number = 60

عملگرهای پیشوند یکانی (Unary prefix) و افزایش/کاهش در کاتلین

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

عملگرمعناعبارتترجمه
+جمع یکانی+a()a.unaryPlus
-منهای یکانی ( معکوس‌سازی علامت)-a()a.unaryMinus
!نه (معکوس مقدار)!a()a.not
++افزایش به مقدار 1 واحد++a()a.inc
--کاهش به مقدار 1 واحد--a()a.dec

مثالی از عملگرهای یکانی

1fun main(args: Array<String>) {
2    val a = 1
3    val b = true
4    var c = 1
5
6    var result: Int
7    var booleanResult: Boolean
8
9    result = -a
10    println("-a = $result")
11
12    booleanResult = !b
13    println("!b = $booleanResult")
14
15    --c
16    println("--c = $c")
17}

کد فوق خروجی زیر را تولید می‌کند:

-a = -1
!b = false
--c = 0

عملگرهای مقایسه و برابری

در ادامه جدولی از عملگرهای برابری و مقایسه‌ای را به همراه معنا و تابع‌های متناظرشان می‌بینید:

عملگرمعناعبارتترجمه
>بزرگ‌تر ازa > ba.compareTo(b) > 0
<کوچک‌تر ازa < ba.compareTo(b) < 0
>=بزرگ‌تر مساویa >= ba.compareTo(b) >= 0
<=کوچک‌تر مساویa < = ba.compareTo(b) <= 0
==برابر است باa == ba?.equals(b) ?: (b === null)
!=برابر نیست باa != b!(a?.equals(b) ?: (b === null))

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

مثالی از عملگرهای مقایسه‌ای و برابری

1fun main(args: Array<String>) {
2
3    val a = -12
4    val b = 12
5
6    // use of greater than operator
7    val max = if (a > b) {
8        println("a is larger than b.")
9        a
10    } else {
11        println("b is larger than a.")
12        b
13    }
14
15    println("max = $max")
16}

خروجی اجرای کد فوق به صورت زیر است:

b is larger than a.
max = 12

عملگرهای منطقی

دو عملگر منطقی به صورت || و && در کاتلین وجود دارند. در ادامه جدولی از عملگرهای منطقی، معنا و تابع‌های متناظر آن‌ها را مشاهده می‌کنید:

عملگرتوصیفعبارتتابع متناظر
||در صورت true بودن هر یک از اجزا، true خواهد بود.(a>b)||(a<c)(a>b)or(a<c
&&در صورت true بودن همه عبارت‌ها مقدار true خواهد بود.(a>b)&&(a<c)(a>b)and(a<c)

توجه کنید که or و and تابع‌هایی هستند که از نمادگذاری میانوندی (infix notation) پشتیبانی می‌کنند. عملگرهای منطقی در گردش کنترل در عبارت‌هایی مانند if و when و همچنین حلقه‌ها کاربرد دارند.

مثالی از عملگرهای منطقی

1fun main(args: Array<String>) {
2
3    val a = 10
4    val b = 9
5    val c = -1
6    val result: Boolean
7
8    // result is true is a is largest
9    result = (a>b) && (a>c) // result = (a>b) and (a>c)
10    println(result)
11}

خروجی اجرای کد فوق به صورت زیر است:

true

عملگر in

عملگر in برای بررسی این که یک شیء به یک مجموعه تعلق دارد یا نه، استفاده می‌شود.

عملگرعبارتترجمه
ina in bb.contains(a)
in!a !in bb.contains(a)!

مثالی از عملگر in

1fun main(args: Array<String>) {
2
3    val numbers = intArrayOf(1, 4, 42, -3)
4
5    if (4 in numbers) {
6        println("numbers array contains 4.")
7    }
8}

نتیجه اجرای کد فوق به صورت زیر است:

numbers array contains 4.

عملگر دسترسی اندیس

در این بخش برخی عبارت‌ها که در عملگر دسترسی اندیس استفاده می‌شوند، به همراه تابع متناظرشان در کاتلین معرفی شده‌اند.

عبارتترجمه
a[i]a.get(i)
a[i, n]a.get(i, n)
a[i1, i2, ..., in]a.get(i1, i2, ..., in)
a[i] = ba.set(i, b)
a[i, n] = ba.set(i, n, b)
a[i1, i2, ..., in] = ba.set(i1, i2, ..., in, b)

مثالی از عملگر دسترسی اندیس

1fun main(args: Array<String>) {
2
3    val a  = intArrayOf(1, 2, 3, 4, - 1)
4    println(a[1])   
5    a[1]= 12
6    println(a[1])
7}

با اجرای کد فوق نتیجه زیر حاصل می‌شود:

2

12

عملگر invoke

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

عبارتترجمه
a()a.invoke()
a(i)a.invoke(i)
a(i1, i2, ..., in)a.inkove(i1, i2, ..., in)
a[i] = ba.set(i, b)
a[i, n] = ba.set(i, b)

در کاتلین پرانتزها به فراخوانی تابع عضو invoke ترجمه می‌شوند.

عملگرهای بیتی

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

  • shl - شیفت چپ علامت‌دار
  • shr - شیفت راست علامت‌دار
  • ushr - شیفت راست بی‌علامت
  • and - و بیتی
  • or - یای بیتی
  • xor - xor بیتی
  • Inv - معکوس بیتی

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

تبدیل نوع در کاتلین

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

1int number1 = 55;
2long number2 = number1;// Valid code

در مثال فوق، مقدار number1 از نوع int به صورت خودکار به نوع long تبدیل می‌شود و به متغیر number2 انتساب می‌یابد.

از سوی دیگر در کاتلین به صورت زیر عمل می‌کنیم:

1val number1: Int = 55
2val number2: Long = number1// Error: type mismatch.

حتی با این که اندازه Long بزرگ‌تر از Int است، کاتلین به صورت خودکار Int را به Long تبدیل نمی‌کند. بلکه باید از به صورت صریح از متد ()toLong استفاده کنیم تا نوع متغیر را به Long تبدیل کنیم. کاتلین از این کار برای جلوگیری از شگفت‌زده کردن کاربر امتناع می‌کند.

1val number1: Int = 55
2val number2: Long = number1.toLong()

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

  • toByte()
  • toShort()
  • toInt()
  • toLong()
  • toFloat()
  • toDouble()
  • toChar()

توجه کنید که تابعی برای تبدیل انواع Boolean وجود ندارد.

تبدیل از نوع بزرگ‌تر به نوع کوچک‌تر

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

1fun main(args : Array<String>) {
2    val number1: Int = 545344
3    val number2: Byte = number1.toByte()
4    println("number1 = $number1")
5    println("number2 = $number2")
6}

خروجی اجرای کد فوق به صورت زیر است:

number1 = 545344
number2 = 64

عبارت‌ها، گزاره‌ها و بلوک‌های کاتلین

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

عبارت‌های کاتلین

«عبارت» (Expression‌) شامل متغیر، عملگر و هر چیزی است که یک مقدار منفرد را ارزیابی می‌کند. به مثال زیر توجه کنید:

val score: Int
score = 90 + 25

در کد فوق، 90 + 25 یک عبارت است که مقدار int بازگشت می‌دهد. توجه کنید که در کاتلین if برخلاف جاوا یک عبارت است. در جاوا if یک گزاره محسوب می‌شود.

1fun main(args: Array<String>) {
2
3    val a = 12
4    val b = 13
5    val max: Int
6
7    max = if (a > b) a else b
8    println("$max")
9}

در مثال فوق if (a > b) a else b یک عبارت است. مقدار این عبارت به متغیر max انتساب می‌یابد.

گزاره‌های کاتلین

«گزاره» (Statement) به هر چیزی گفته می‌شود که یک واحد ترکیبی اجرایی را تشکیل می‌دهد. به مثال زیر توجه کنید:

val score = 90 + 25

در کد فوق 90+25 یک عبارت است که مقدار 115 بازگشت می‌دهد، اما ;val score = 9*5 یک گزاره است. عبارت‌ها بخشی از گزاره‌ها هستند. در ادامه برخی مثال‌ها در این خصوص ارائه شده‌اند:

println("Howdy")
var a = 5
++a
max = if (a > b) a else b

بلوک‌های کاتلین

در کاتلین، بلوک به گروهی از گزاره‌ها گفته می‌شود که درون آکولاد {} قرار می‌گیرند. به مثال زیر توجه کنید:

1fun main(args: Array<String>) {  // main function block
2    val flag = true
3
4    if (flag == true) {      // start of if block
5        print("Hey ")
6        print("jude!")
7    }                        // end of if block
8}                            // end of main function block

در کد فوق دو گزاره print("Hey ") و print(" jude!") درون بلوک if قرار دارند.

print("Hey ")
print("jude!")

به طور مشابه تابع ()main نیز یک بدنه بلوک دارد.

1val flag = true
2
3if (flag == true) {      // start of block
4    print("Hey ")
5    print("jude!")
6}                        // end of block

کامنت‌ها در کاتلین

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

کامنت‌ها بخشی از برنامه هستند که به منظور درک بهتر کد چه برای خود برنامه‌نویس و چه افراد دیگری که کد را می‌خوانند مورد استفاده قرار می‌گیرند. کامنت‌ها از سوی کامپایلر کاتلین به طور کامل نادیده گرفته می‌شوند. همانند جاوا دو روش برای درج کامنت در کاتلین وجود دارد:

  • /* ... */
  • // ....

روش سنتی درج کامنت

برای درج کامنت‌های چندخطی که در خطوط مختلفی نوشته می‌شوند، باید از نمادهای /* ... */ استفاده کنید. کامپایلر کاتلین هر چیزی را که بین /* و */ قرار داشته باشد، نادیده می‌گیرد. به مثال زیر توجه کنید:

1/* This is a multi-line comment.
2 * The problem prints "Hello, World!" to the standard output.
3 */
4fun main(args: Array<String>) {
5
6   println("Hello, World!")
7}

از روش سنتی درج کامنت با کمی تغییر برای مستندسازی کد کاتلین (KDoc) نیز استفاده می‌شود. کامنت‌های KDoc با /** آغاز یافته و با/** خاتمه می‌یابند.

کامنت ته خط

برای درج کامنت در انتهای یک خط از برنامه باید از کاراکترهای // استفاده کنید. کامپایلر کاتلین این کاراکترهای // و هر چه پس از آن می‌ْآید را نادیده می‌گیرد. به مثال زیر توجه کنید:

1// Kotlin Hello World Program
2fun main(args: Array<String>) {
3
4   println("Hello, World!")      // outputs Hello, World! on the screen
5}

برنامه فوق شامل دو کامنت ته خط است:

// Kotlin Hello World Program

و

// outputs Hello, World! on the screen

استفاده از کامنت‌ها به روش صحیح

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

برخی نیز بر این باورند که کد باید خود-گویا باید و می‌بایست از کامنت‌ها به ندرت استفاده کنیم. با این حال، دیدگاه عمومی با این نظر مخالف است. استفاده از کامنت در کد برای توضیح الگوریتم‌های پیچیده، regex یا تبیین سناریوهایی که از یک تکنیک به جای تکنیک‌های دیگر برای حل مسئله استفاده کنیم، هیچ اشکالی ندارد. در اغلب موارد باید از کامنت‌ها به منظور توضیح چرایی و نه چگونگی کد استفاده کنیم.

ورودی/خروجی ابتدایی کاتلین

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

خروجی کاتلین

امکان استفاده از تابع‌های ()println و ()print برای ارسال خروجی به خروجی استاندارد (صفحه نمایش) وجود دارد. به مثال زیر توجه کنید:

1fun main(args : Array<String>) {
2    println("Kotlin is interesting.")
3}

زمانی که این برنامه اجرا شود، خروجی به صورت زیر روی صفحه ظاهر می‌شود:

Kotlin is interesting.

در این کد ()println رشته درون گیومه را در خروجی ارائه می‌کند.

تفاوت بین ()println و ()print

  • تابع ()print رشته درون گیومه را پرینت می‌کند.
  • تابع ()println رشته درون گیومه را مشابه تابع ()print پرینت می‌کند. سپس کرسر به ابتدای خط بعدی می‌رود.

زمانی که از تابع ()println استفاده می‌کنیم، تابع ()System.out.println به صورت داخلی فراخوانی می‌شود. این تابع به منظور نمایش خروجی روی صفحه در جاوا استفاده می‌شود.

اگر از IntelliJ IDEA استفاده می‌کنید کرسر ماوس را در کنار عبارت println قرار داده و به منوی Navigate > Declaration قرار دهید. همچنین می‌توانید کلیدهای ترکیبی Ctrl+B (در مک کلیدهای Cmd+B) را بزنید. به این ترتیب فایل اعلان به نام Console.kt باز می‌شود. در این فایل می‌توانید ببینید که تابع ()println به صورت داخلی ()System.out.println را فراخوانی می‌کند. به طور مشابه زمانی که از تابع ()print استفاده می‌کنید، تابع ()System.out.print را در جاوا فراخوانی می‌کند.

مثال اول: ()print و ()println

1fun main(args : Array<String>) {
2    println("1. println ");
3    println("2. println ");
4
5    print("1. print ");
6    print("2. print");
7}

خروجی برنامه فوق به صورت زیر است:

1. println
2. println
1. print 2. Print

مثال دوم: پرینت متغیرها و مقادیر لفظی

1fun main(args : Array<String>) {
2    val score = 12.3
3
4    println("score")
5    println("$score")
6    println("score = $score")
7    println("${score + score}")
8    println(12.3)
9}

خروجی برنامه فوق نیز به صورت زیر است:

score

12.3

score = 12.3

24.6

12.3

ورودی در کاتلین

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

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

1fun main(args: Array<String>) {
2    print("Enter text: ")
3
4    val stringInput = readLine()!!
5    println("You entered: $stringInput")
6}

خروجی برنامه فوق به صورت زیر است:

Enter text: Hmm, interesting!
You entered: Hmm, interesting!

ما می‌توانیم ورودی یک کاربر را به صورت یک رشته با استفاده از تابع ()readLine بگیریم و آن را به صورت صریح به مقادیر نوع دیگر مانند Int تبدیل کنیم.

اگر می‌خواهید ورودی‌های کاربر را با انواع دیگری از داده بگیرید، می‌توانید از شیء scanner استفاده کنید. به این منظور باید کلاس scanhner را از کتابخانه استاندارد جاوا ایمپورت کنید:

1import java.util.Scanner

سپس باید شیء Scanner را از این کلاس بسازید.

1val reader = Scanner(System.`in`)

اکنون شیء reader می‌تواند برای دریافت ورودی از کاربر مورد استفاده قرار گیرد.

مثال چهارم: دریافت عدد صحیح ورودی از سوی کاربر

1import java.util.Scanner
2
3fun main(args: Array<String>) {
4
5    // Creates an instance which takes input from standard input (keyboard)
6    val reader = Scanner(System.`in`)
7    print("Enter a number: ")
8
9    // nextInt() reads the next integer from the keyboard
10    var integer:Int = reader.nextInt()
11
12    println("You entered: $integer")
13}

با اجرای برنامه فوق، خروجی زیر تولید می‌شود:

Enter a number: -12

You entered: -12

در این کد، شیء reader از کلاس Scanner ایجاد می‌شود. سپس متد ()nextInt فراخوانی می‌شود که یک ورودی صحیح از کاربر می‌گیرد و آن را در متغیر Integer ذخیره می‌کند.

برای دریافت ورودی‌های از نوع Long ،Float ،double و Boolean از کاربر می‌توانیم به ترتیب از متدهای ()nextLong() ،nextFloat() ،nextDouble و ()nextBoolean استفاده کنیم.

عبارت if در کاتلین

در این بخش از مقاله آموزش کاتلین به کمک برخی مثال‌ها به بررسی شیوه استفاده از عبارت if در کاتلین می‌پردازیم.

کاربرد سنتی if...else

ساختار if...else به صورت زیر است:

1if (testExpression) {
2   // codes to run if testExpression is true
3}
4else {
5  // codes to run if testExpression is false
6}

عبارت if در صورتی که مقدار testExpression به صورت true ارزیابی شود، بخش خاصی از کد را اجرا می‌کند. در صورتی یک بند اختیاری else وجود داشته باشد، کدهای درون بند else در صورتی اجرا می‌شوند که مقدار testExpression به صورت false ارزیابی شود.

مثالی از کاربرد سنتی if...else

1fun main(args: Array<String>) {
2
3    val number = -10
4
5    if (number > 0) {
6        print("Positive number")
7    } else {
8        print("Negative number")
9    }
10}

خروجی اجرای کد فوق به صورت زیر است:

Negative number

عبارت if در کاتلین

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

مثالی از عبارت if در کاتلین

1fun main(args: Array<String>) {
2
3    val number = -10
4
5    val result = if (number > 0) {
6        "Positive number"
7    } else {
8        "Negative number"
9    }
10
11    println(result)
12}

با اجرای کد فوق، خروجی زیر روی صفحه نمایش می‌یابد:

Negative number

شاخه esle کد در صورت استفاده از if به صورت یک عبارت، ضروری خواهد بود. اگر بدنه if مانند مثال زیر، تنها یک گزاره داشته باشد، استفاده از آکولادها، اختیاری خواهد بود:

1fun main(args: Array<String>) {
2    val number = -10
3    val result = if (number > 0) "Positive number" else "Negative number"
4    println(result)
5}

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

مثالی از بلوک if با عبارت‌های چندگانه

اگر یک شاخه از بلوک if شامل بیش از یک عبارت باشد، آخرین عبارت به عنوان مقدار بلوک بازگشت می‌یابد.

1fun main(args: Array<String>) {
2
3    val a = -9
4    val b = -11
5
6    val max = if (a > b) {
7        println("$a is larger than $b.")
8        println("max variable holds value of a.")
9        a
10    } else {
11        println("$b is larger than $a.")
12        println("max variable holds value of b.")
13        b
14    }
15    println("max = $max")
16}

خروجی کد فوق به صورت زیر است:

-9 is larger than -11.

max variable holds value of a.

max = -9

ساختار if..else..if در کاتلین

در کاتلین امکان بازگشت یک بلوک از کد در میان بلوک‌های متعدد به صورت زیر با استفاده از ساختار if..else...if وجود دارد.

مثالی از ساختار if..else...if

1fun main(args: Array<String>) {
2
3    val number = 0
4
5    val result = if (number > 0)
6        "positive number"
7    else if (number < 0)
8        "negative number"
9    else 
10        "zero"
11    
12    println("number is $result")
13}

برنامه بررسی می‌کند که آیا Number یک عدد مثبت، یک عدد منفی یا صفر است.

عبارت if تودرتو در کاتلین

بک عبارت if را می‌توان درون بلوک یک عبارت if دیگر قرار دارد. این وضعیت به نام عبارت if تودرتو شناخته می‌شود.

مثالی از عبارت if تودرتو

برنامه زیر بزرگ‌ترین عدد را در میان سه عدد پیدا می‌کند.

1fun main(args: Array<String>) {
2
3    val n1 = 3
4    val n2 = 5
5    val n3 = -2
6
7    val max = if (n1 > n2) {
8        if (n1 > n3)
9            n1
10        else
11            n3
12    } else {
13        if (n2 > n3)
14            n2
15        else
16            n3
17    }
18
19    println("max = $max")
20}

خروجی کد فوق به صورت زیر است:

max = 5

عبارت when در کاتلین

در این بخش از مقاله آموزش کاتلین با سازه when آشنا شده و برخی مثال‌های کاربردی آن را بررسی می‌کنیم.

سازه when در کاتلین

سازه when در کاتلین را می‌توان به عنوان جایگزینی برای گزاره switch در جاوا تصور کرد. این سازه بخشی از کد را در برابر جایگزین‌های متعدد ارزیابی می‌کند.

مثالی از یک عبارت ساده when

1fun main(args: Array<String>) {
2
3    val a = 12
4    val b = 5
5
6    println("Enter operator either +, -, * or /")
7    val operator = readLine()
8
9    val result = when (operator) {
10        "+" -> a + b
11        "-" -> a - b
12        "*" -> a * b
13        "/" -> a / b
14        else -> "$operator operator is invalid operator."
15    }
16
17    println("result = $result")
18}

خروجی کد فوق مانند زیر است:

Enter operator either +, -, * or /
*
result = 60

برنامه فوق یک رشته ورودی از کاربر می‌گیرد. فرض کنید کاربر مقدار * وارد می‌کند. در این حالت، عبارت a*b ارزیابی می‌شود و مقدار مورد نظر به یک متغیر به نام result انتساب می‌یابد.

اگر هیچ کدام از شاخه‌های شرطی برقرار نباشند، یعنی کاربر چیزی به جز +، -، * یا / وارد کرده باشد، در این صورت شاخه else ارزیابی می‌شود.

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

1fun main(args: Array<String>) {
2
3    val a = 12
4    val b = 5
5
6    println("Enter operator either +, -, * or /")
7    val operator = readLine()
8
9    when (operator) {
10        "+" -> println("$a + $b = ${a + b}")
11        "-" -> println("$a - $b = ${a - b}")
12        "*" -> println("$a * $b = ${a * b}")
13        "/" -> println("$a / $b = ${a / b}")
14        else -> println("$operator is invalid")
15    }
16}

خروجی کد فوق به صورت زیر است:

Enter operator either +, -, * or /
-
12 - 5 = 7

در این برنامه when یک عبارت نیست، چون مقدار بازگشتی آن به هیچ چیز انتساب نمی‌یابد. در این حالت، وجود شاخه else ضرورتی ندارد.

کاربردهای مختلف when در کاتلین

امکان ترکیب دو یا چند شرط با استفاده از کاما وجود دارد. به مثال زیر توجه کنید:

1fun main(args: Array<String>) {
2
3    val n = -1
4
5    when (n) {
6        1, 2, 3 -> println("n is a positive integer less than 4.")
7        0 -> println("n is zero")
8        -1, -2 -> println("n is a negative integer greater than 3.")
9    }
10}

خروجی برنامه فوق به صورت زیر است:

n is a negative integer greater than 3.

با استفاده از when امکان بررسی وجود مقداری در یک بازه فراهم می‌شود. به مثال زیر توجه کنید:

1fun main(args: Array<String>) {
2
3    val a = 100
4
5    when (a) {
6        in 1..10 -> println("A positive number less than 11.")
7        in 10..100 -> println("A positive number between 10 and 100 (inclusive)")
8    }
9}

خروجی کد فوق به صورت زیر است:

A positive number between 10 and 100 (inclusive)

بررسی نوع یک مقدار

برای این که در زمان اجرا بررسی کنیم یک مقدار دارای نوع خاصی است یا نه، می‌توانیم از عملگرهای is و ‎!is استفاده کنیم. به مثال زیر توجه کنید:

1when (x) {
2    is Int -> print(x + 1)
3    is String -> print(x.length + 1)
4    is IntArray -> print(x.sum())
5}

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

1fun main(args: Array<String>) {
2
3    val a = 11
4    val n = "11"
5
6    when (n) {
7        "cat" -> println("Cat? Really?")
8        12.toString() -> println("Close but not close enough.")
9        a.toString() -> println("Bingo! It's eleven.")
10    }
11}

خروجی برنامه فوق به صورت زیر است:

Bingo! It's eleven.

حلقه while و do...while در کاتلین

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

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

در ادامه این مقاله با بررسی مثال‌هایی با دو نوع حلقه while و do...while در زبان کاتلین آشنا می‌شویم. اگر با حلقه‌های while و do...while در جاوا آشنا باشید، باید بدانید که عملکرد این حلقه‌ها در کاتلین نیز دقیقاً به همان ترتیب است.

حلقه while در کاتلین

ساختار حلقه while به صورت زیر است:

1while (testExpression) {
2    // codes inside body of while loop
3}

طرز کار حلقه while چگونه است؟

عبارت تست درون پرانتز یک عبارت بولی است. اگر عبارت تست به صورت true ارزیابی شود:

  • گزاره درون عبارت while اجرا می‌شود.
  • سپس عبارت تست دوباره ارزیابی می‌شود.

این فرایند تا زمانی که عبارت test به صورت false ارزیابی شود، تداوم می‌یابد. در این حالت حلقه while خاتمه می‌یابد.

فلوچارت حلقه While در کاتلین

آموزش کاتلین

مثالی از حلقه while در کاتلین

1// Program to print line 5 times
2
3fun main(args: Array<String>) {
4
5    var i = 1
6
7    while (i <= 5) {
8        println("Line $i")
9        ++i
10    }
11}

با اجرای برنامه فوق، خروجی زیر ایجاد می‌شود:

Line 1
Line 2
Line 3
Line 4
Line 5

توجه کنید که گزاره i++ درون حلقه while قرار دارد. پس از 5 بار تکرار، متغیر i به مقدار 6 افزایش می‌یابد. در این زمان عبارت تست به صورت i<=5 به صورت false ارزیابی می‌شود و حلقه خاتمه می‌یابد. اگر بدنه حلقه تنها یک گزاره داشته باشد، لزومی به استفاده از آکولاد {} وجود ندارد.

مثالی از محاسبه مجموع اعداد طبیعی

1// Program to compute the sum of natural numbers from 1 to 100.
2fun main(args: Array<String>) {
3
4    var sum = 0
5    var i = 100
6
7    while (i != 0) {
8        sum += i     // sum = sum + i;
9        --i
10    }
11    println("sum = $sum")
12}

خروجی برنامه فوق به صورت زیر است:

sum = 5050

در برنامه فوق متغیر sum با مقدار 0 آغاز می‌شود و i در ابتدا دارای مقدار 100 است. در هر بار تکرارِ حلقه while، متغیر sum به مقدار sum+1 انتساب می‌یابد و مقدار به اندازه 1 واحد کاهش می‌یابد تا این که i باربر با 0 باشد. برای تصور بهتر به نمونه اجرای زیر توجه کنید:

1st iteration: sum = 0+100 = 100, i = 99
2nd iteration: sum = 100+99 = 199, i = 98
3rd iteration: sum = 199+98 = 297, i = 97
... .. ...
... .. ...
99th iteration: sum = 5047+2 = 5049, i = 1
100th iteration: sum = 5049+1 = 5050, i = 0 (then loop terminates)

در بخش عملگرهای مقایسه و منطقی این مقاله در خصوص عبارت‌های تست بیشتر توضیح داده‌ایم.

حلقه do...while در کاتلین

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

1do {
2   // codes inside body of do while loop
3} while (testExpression);

طرز کار حلقه do...while چگونه است؟

کد درون بدنه سازه do یک بار و بدون بررسی عبارت تست (testExpression)‌ اجرا می‌شود. سپس عبارت تست بررسی می‌شود. اگر عبارت تست به صورت true ارزیابی شود، کدهای درون بدنه حلقه اجرا می‌شوند و عبارت تست دوباره ارزیابی می‌شود. این فرایند تا زمانی که عبارت تست به صورت false ارزیابی شود ادامه می‌یابد. زمانی که عبارت تست به صورت false ارزیابی شود، حلقه do..while خاتمه می‌یابد.

فلوچارت حلقه do..while

آموزش کاتلین

مثالی از حلقه do..while

برنامه زیر مجموع اعداد وارد شده از سوی کاربر را تا زمانی که کاربر عدد 0 را وارد کند، محاسبه می‌کند. برای دریافت ورودی کاربر از تابع ()readline استفاده شده است.

1fun main(args: Array<String>) {
2
3    var sum: Int = 0
4    var input: String
5
6    do {
7        print("Enter an integer: ")
8        input = readLine()!!
9        sum += input.toInt()
10
11    } while (input != "0")
12
13    println("sum = $sum")
14}

خروجی برنامه فوق به صورت زیر است:

Enter an integer: 4
Enter an integer: 3
Enter an integer: 2
Enter an integer: -6
Enter an integer: 0
sum = 3

حلقه for در کاتلین

در این بخش از مقاله آموزش کاتلین با حلقه for در این زبان برنامه‌نویسی آشنا خواهیم شد. حلقه for در کاتلین برای تکرار یک قطعه کد به تعداد مشخصی دفعه استفاده می‌شود.

برخلاف جاوا و دیگر زبان‌های رایج برنامه‌نویسی، هیچ حلقه for در کاتلین به طور سنتی وجود ندارد. بلکه در کاتلین حلقه for برای تکرار روی بازه‌ها، آرایه‌ها، map-ها و مواردی از این دست استفاده می‌شود. در واقع حلقه for روی هر چیزی که یک «تکرارکننده» (iterator) داشته باشد اجرا می‌شود. ساختار حلقه for در کاتلین به صورت زیر است:

1for (item in collection) {
2    // body of loop
3}

مثالی از تکرار روی یک بازه

1fun main(args: Array<String>) {
2
3    for (i in 1..5) {
4        println(i)
5    }
6}

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

1
2
3
4
5

اگر بدنه حلقه مانند مثال فوق، شامل تنها یک گزاره باشد، لزومی به استفاده از آکولاد {} نیست.

1fun main(args: Array<String>) {
2    for (i in 1..5) println(i)
3}

امکان تکرار روی یک بازه با استفاده از حلقه for به این جهت فراهم آمده است که بازه‌ها یک «تکرارکننده» (iterator) ارائه می‌کنند. در خصوص تکرارکننده‌ها در ادامه این مقاله مطالب بیشتری ارائه شده است.

مثالی از روش‌های مختلف تکرار روی یک بازه

1fun main(args: Array<String>) {
2
3    print("for (i in 1..5) print(i) = ")
4    for (i in 1..5) print(i)
5
6    println()
7
8    print("for (i in 5..1) print(i) = ")
9    for (i in 5..1) print(i)             // prints nothing
10
11    println()
12
13    print("for (i in 5 downTo 1) print(i) = ")
14    for (i in 5 downTo 1) print(i)
15
16    println()
17
18    print("for (i in 1..4 step 2) print(i) = ")
19    for (i in 1..5 step 2) print(i)
20
21    println()
22
23    print("for (i in 4 downTo 1 step 2) print(i) = ")
24    for (i in 5 downTo 1 step 2) print(i)
25}

خروجی برنامه فوق به صورت زیر است:

for (i in 1..5) print(i) = 12345
for (i in 5..1) print(i) =
for (i in 5 downTo 1) print(i) = 54321
for (i in 1..4 step 2) print(i) = 135
for (i in 4 downTo 1 step 2) print(i) = 531

تکرار روی یک آرایه

در این بخش مثالی از تکرار روی یک آرایه String را بررسی می‌کنیم:

1fun main(args: Array<String>) {
2
3    var language = arrayOf("Ruby", "Koltin", "Python" "Java")
4
5    for (item in language)
6        println(item)
7}

خروجی کد فوق به صورت زیر است:

Ruby
Koltin
Python
Java

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

1fun main(args: Array<String>) {
2
3    var language = arrayOf("Ruby", "Koltin", "Python", "Java")
4
5    for (item in language.indices) {
6
7        // printing array elements having even index only
8        if (item%2 == 0)
9            println(language[item])
10    }
11}

خروجی برنامه فوق به صورت زیر است:

Ruby

Python

اگر می‌خواهید در این خصوص اطلاعات بیشتری کسب کنید به بخش «آرایه‌های کاتلین» (+) مراجعه کنید.

تکرار روی یک رشته

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

1fun main(args: Array<String>) {
2
3    var text= "Kotlin"
4
5    for (letter in text) {
6        println(letter)
7    }
8}

خروجی کد فوق به صورت زیر است:

K
o
t
l
i
n

امکان تکرار روی یک String با استفاده از اندیس و به روشی مشابه آرایه‌ها وجود دارد. به مثال زیر توجه کنید:

1fun main(args: Array<String>) {
2
3    var text= "Kotlin"
4
5    for (item in text.indices) {
6        println(text[item])
7    }
8}

خروجی کد فوق به صورت زیر است:

K
o
t
l
i
n

عبارت break در کاتلین

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

طرز کار break چگونه است؟

عبارت berak تقریباً همیشه همراه با سازه if…else استفاده می‌شود. به عنوان مثال به کد زیر توجه کنید:

1for (...) {
2    if (testExpression) {
3        break
4    }
5}

اگر testExpression به صورت true ارزیابی شود، break اجرا می‌شود که حلقه for را خاتمه می‌بخشد.

آموزش کاتلین

مثالی از break در کاتلین

1fun main(args: Array<String>) {
2
3    for (i in 1..10) {
4        if (i == 5) {
5            break
6        }
7        println(i)
8    }
9}

خروجی کد فوق به صورت زیر است:

1
2
3
4

زمانی که مقدار i برابر با 5 شود، عبارت i == 5 درون if به صورت true ارزیابی می‌شود و break اجرا خواهد شد. این ترتیب اجرای حلقه for خاتمه می‌یابد.

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

برنامه زیر مجموع اعداد وارد شده از سوی کاربر را تا زمانی که وی عدد 0 وارد کند، محاسبه می‌کند. برای کسب اطلاعات بیشتر در خصوص روش دریافت ورودی از کاربر به بخش «ورودی/خروجی ابتدایی» در کاتلین مراجعه کنید.

1fun main(args: Array<String>) {
2
3    var sum = 0
4    var number: Int
5
6    while (true) {
7        print("Enter a number: ")
8        number = readLine()!!.toInt()
9
10        if (number == 0)
11            break
12
13        sum += number
14    }
15
16    print("sum = $sum")
17}

خروجی کد فوق به صورت زیر است:

Enter a number: 4
Enter a number: 12
Enter a number: 6
Enter a number: -9
Enter a number: 0
sum = 13

در برنامه فوق، عبارت تست حلقه while همواره به صورت true ارزیابی می‌شود. در این برنامه حلقه while تا زمانی اجرا می‌شود که کاربر عدد 0 را وارد کند. زمانی که کاربر عدد 0 وارد کند، break اجرا شده و حلقه while خاتمه می‌یابد.

برچسب break در کاتلین

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

طرز کار break برچسب‌دار چگونه است؟

آموزش کاتلین

برچسب‌ها در کاتلین با یک شناسه آغاز شده و سپس یک علامت @ می‌آید. در مثال زیر @test یک برچسب است که برای نشانه‌گذاری حلقه while بیرونی استفاده می‌شود. بدین ترتیب با استفاده از یک عبارت break به همراه برچسب به صورت break@test می‌تواند حلقه خاصی را متوقف کنید:

1fun main(args: Array<String>) {
2
3    first@ for (i in 1..4) {
4
5        second@ for (j in 1..2) {
6            println("i = $i; j = $j")
7
8            if (i == 2)
9                break@first
10        }
11    }
12}

خروجی کد فوق به صورت زیر است:

i = 1; j = 1
i = 1; j = 2
i = 2; j = 1

در مثال فوق، عبارت i==2 به صورت true ارزیابی می‌شود و break@first اجرا می‌شود که موجب خاتمه یافتن حلقه با نشانه @first می‌شود. در مثال زیر یک نسخه کمی متفاوت از برنامه فوق را می‌بینید. در برنامه زیر break حلقه با نشانه @second را خاتمه می‌بخشد.

1fun main(args: Array<String>) {
2
3    first@ for (i in 1..4) {
4
5        second@ for (j in 1..2) {
6            println("i = $i; j = $j")
7
8            if (i == 2)
9                break@second
10        }
11    }
12}

خروجی برنامه فوق به صورت زیر است:

i = 1; j = 1
i = 1; j = 2
i = 2; j = 1
i = 3; j = 1
i = 3; j = 2
i = 4; j = 1
i = 4; j = 2

نکته: از آنجا که break در این برنامه برای خاتمه بخشیدن به درونی‌ترین حلقه استفاده شده است، در این حالت، لزومی به استفاده از break برچسب‌دار وجود ندارد.

به طور کلی سه عبارت پرش به صورت break ،continue و return در کاتلین وجود دارند. در این بخش از مقاله آموزش کاتلین با عبارت break آشنا شدیم. عبارت‌های پرشی continue و return نیز در بخش‌های بعدی این مقاله ارائه شده‌اند.

عبارت continue در کاتلین

در این بخش از مقاله آموزش کاتلین، از عبارت continue برای پرش و رد شدن از تکرار کنونی حلقه استفاده می‌کنیم. ضمناً در این بخش از مقاله با برچسب‌های continue نیز آشنا خواهیم شد.

فرض کنید مشغول کار روی حلقه‌ها هستید. برخی اوقات لازم می‌شود که تکرار کنونی حلقه را رد کنیم. در یک چنین حالتی، از عبارت continue استفاده می‌کنیم. سازه continue موجب می‌شود که تکرار کنونی درون حلقه رد شود (اجرا نمی‌شود) و کنترل برنامه به انتهای بدنه حلقه منتقل می‌شود.

طرز کار continue چگونه است؟

عبارت continue تقریباً همیشه همراه با سازه if...else استفاده می‌شود. به مثال زیر توجه کنید:

1while (testExpression1) {
2
3    // codes1
4    if (testExpression2) {
5        continue
6    }
7    // codes2
8}

اگر مقدار testExpression2 به صورت true ارزیابی شود، سازه continue اجرا می‌شود و همه کدهای درون حلقه while برای آن دفعه تکرار رد می‌شود (اجرا نخواهد شد و به گام بعدی تکرار می‌رود).

آموزش کاتلین

مثالی از continue در کاتلین

1fun main(args: Array<String>) {
2
3    for (i in 1..5) {
4        println("$i Always printed.")
5        if (i > 1 && i < 5) {
6            continue
7        }
8        println("$i Not always printed.")
9    }
10}

خروجی کد فوق به صورت زیر است:

1 Always printed.
1 Not always printed.
2 Always printed.
3 Always printed.
4 Always printed.
5 Always printed.
5 Not always printed.

زمانی که مقدار i بزرگ‌تر از 1 و کمتر از 5 باشد، continue اجرا می‌شود که موجب اجرا نشدن گزاره زیر می‌شود:

println("$i Not always printed.")

با این حال گزاره زیر در هر تکرار حلقه اجرا می‌شود، زیرا این گزاره پیش از سازه continue قرار دارد:

println("$i Always printed.")

مثالی از محاسبه مجموع صرف اعداد مثبت

برنامه زیر مجموع حداکثر 6 عدد را که از سوی کاربر وارد می‌شوند، محاسبه می‌کند. اگر کاربر عدد منفی یا صفر وارد کند، از محاسبه صرف‌نظر می‌شود.

1fun main(args: Array<String>) {
2
3    var number: Int
4    var sum = 0
5
6    for (i in 1..6) {
7        print("Enter an integer: ")
8        number = readLine()!!.toInt()
9
10        if (number <= 0)
11            continue
12        
13        sum += number
14    }
15    println("sum = $sum")
16}

خروجی کد فوق به صورت زیر است:

Enter an integer: 4
Enter an integer: 5
Enter an integer: -50
Enter an integer: 10
Enter an integer: 0
Enter an integer: 12
sum = 31

Continue برچسب‌دار در کاتلین

هر آنچه تا کنون در خصوص continue گفتیم به شکل بدون برچسب آن مربوط می‌شد. این نوع از سازه continue از اجرای تکرار جاری نزدیک‌ترین حلقه محصور جلوگیری می‌کند. اما می‌توان از continue برای رد شدن از تکرار هر حلقه مورد نظر (و نه لزوماً نزدیک‌ترین حلقه) استفاده کرد. بدین منظور باید از برچسب‌های continue استفاده کنیم.

طرز کار continue برچسب‌دار چگونه است؟

آموزش کاتلین

برچسب‌ها در کاتلین با یک شناسه آغاز می‌شوند و سپس کاراکتر @ می‌آید. در برنامه زیر از برچسب @outerloop استفاده می‌کنیم که حلقه while بیرونی را نشانه‌گذاری می‌کند. اینک با استفاده از continue به همراه برچسب continue@outerloop می‌توانید از اجرای کدهای یک حلقه خاص برای آن دفعه تکرار جلوگیری کنید.

مثالی از continue برچسب‌دار

1fun main(args: Array<String>) {
2
3    here@ for (i in 1..5) {
4        for (j in 1..4) {
5            if (i == 3 || j == 2)
6                continue@here
7            println("i = $i; j = $j")
8        }
9    }
10}

خروجی کد فوق به صورت زیر است:

i = 1; j = 1
i = 2; j = 1
i = 4; j = 1
i = 5; j = 1

استفاده از continue برچسب‌دار غالباً توصیه نمی‌شود، زیرا موجب دشواری درک کد می‌شود. اگر در موقعیتی هستید که مجبور به استفاده از continue برچسب‌دار هستید، می‌توانید کد خود را با تلاش برای یافتن روش متفاوتی که خوانایی بیشتری داشته باشد، «بازسازی» (refactor) کنید.

در بخش بعدی به بررسی آخرین عبارت پرشی کاتلین یعنی return می‌پردازیم.

تابع‌های کاتلین

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

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

تابع‌ها برای تجزیه یک برنامه بزرگ به اجزای کوچک‌تر و ایجاد حالت ماژولار مورد استفاده قرار می‌گیرند. برای نمونه فرض کنید باید یک دایره را بر اساس ورودی از سوی کاربر ایجاد و رنگ‌آمیزی کنید. به این منظور می‌توانید دو تابع بنویسید که به ترتیب عملیات ایجاد دایره و رنگ‌آمیزی آن را اجرا می‌کنند:

  • تابع ()createCircle
  • تابع ()colorCircl

تقسیم یک برنامه بزرگ به کامپوننت‌های کوچک‌تر، موجب می‌شود که برنامه قابلیت سازمان‌دهی و مدیریت بیشتری پیدا کند.

انواع تابع‌ها

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

  • تابع کتابخانه استاندارد کاتلین
  • تابع‌های تعریف شده از سوی کاربر

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

تابع‌های کتابخانه استاندارد کاتلین، تابع‌های داخلی این زبان برنامه‌نویسی هستند که به صورت آماده ‌می‌توان از آن‌ها استفاده کرد. برای نمونه به مثال زیر توجه کنید. در این مثال تابع ()print یک تابع کتابخانه استاندارد است که در استریم خروجی استاندارد (نمایشگر) پرینت می‌کند. همچنین تابع ()sqrt جذر عدد را با مقدار از نوع Double بازگشت می‌دهد:

1fun main(args: Array<String>) {
2
3    var number = 5.5
4    print("Result = ${Math.sqrt(number)}")
5}

خروجی برنامه فوق به صورت زیر است:

Result = 2.345207879911715

تابع‌های تعریف ‌شده‌ کاربر

همان طور که پیش‌تر اشاره کردیم، شما می‌توانید تابع‌ها را خودتان نیز ایجاد کنید. چنین تابع‌هایی به نام تابع‌های تعریف‌شده‌ی کاربر شناخته می‌شوند.

شیوه ایجاد تابع تعریف ‌شده‌ کاربر در کاتلین چگونه است؟

پیش از آن که بتوانیم از تابع استفاده (آن را فراخوانی) کنیم، باید آن را تعریف نماییم. روش تعریف تابع در کاتلین به صورت زیر است:

1fun callMe() {
2    // function body
3}

برای تعریف یک تابع در کاتلین از کلیدواژه fun استفاده می‌کنیم. سپس نام تابع (شناسه) می‌آید. در مثال فوق نام تابع callMe است. در برنامه فوق، پرانتزها خالی هستند. این بدان معنی است که این تابع هیچ آرگومانی نمی‌گیرد. در مورد آرگومان‌های تابع در ادامه بیشتر توضیح خواهیم داد.

کدهای درون آکولاد، بدنه تابع را تشکیل می‌دهند.

شیوه فراخوانی تابع چگونه است؟

برای اجرای کدهای درون بدنه تابع باید آن را فراخوانی (Call) کنیم. روش انجام این کار به صورت زیر است:

1callme()

گزاره فوق، تابع ()callme را که پیش‌تر اعلان کرده‌ایم، فرا می‌خواند.

آموزش کاتلین

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

1fun callMe() {
2    println("Printing from callMe() function.")
3    println("This is cool (still printing from inside).")
4}
5
6fun main(args: Array<String>) {
7    callMe()
8    println("Printing outside from callMe() function.")
9}

خروجی برنامه فوق به صورت زیر است:

Printing from callMe() function.
This is cool (still printing from inside).
Printing outside from callMe() function.

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

مثالی از کاربرد تابع برای جمع دو عدد

1fun addNumbers(n1: Double, n2: Double): Int {
2    val sum = n1 + n2
3    val sumInteger = sum.toInt()
4    return sumInteger
5}
6
7fun main(args: Array<String>) {
8    val number1 = 12.2
9    val number2 = 3.4
10    val result: Int
11
12    result = addNumbers(number1, number2)
13    println("result = $result")
14}

خروجی برنامه فوق به صورت زیر است:

result = 15

طرز کار تابع‌های دارای آرگومان و مقدار بازگشتی در کاتلین چگونه است؟

در مثال فوق دو عدد number1 و number2 از نوع Double در طی فراخوانی تابع، به آن ارسال می‌شوند. این آرگومان‌ها به نام آرگومان‌های واقعی شناخته می‌شوند.

result = addNumbers(number1, number2)

پارامترهای n1 و n2 آرگومان‌های ارسالی را می‌گیرند. این آرگومان‌ها به نام آرگومان‌های صوری (یا پارامتر) شناخته می‌شوند.

آموزش کاتلین

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

گزاره بازگشتی این تابع به صورت return sumInteger است. این کد موجب خاتمه یافتن تابع ()addNumbers می‌شود و بدین ترتیب کنترل برنامه به تابع ()main بازمی‌گردد. در این برنامه sumInteger از تابع ()addNumbers بازگشت می‌یابد. این مقدار به متغیر result انتساب می‌یابد.

آموزش کاتلین

توجه کنید که هر دو متغیر sumInteger و result از نوع Int هستند. همچنین نوع بازگشتی تابع در تعریف تابع تعریف می‌شود:

1// return type is Int
2fun addNumbers(n1: Double, n2: Double): Int {
3    ... .. ...
4}

اگر تابع هیچ مقداری بازگشت ندهد، نوع بازگشتی آن Unit خواهد بود. در صوتی که نوع بازگشتی به صورت Unit باشد، تعیین نوع بازگشتی در تعریف تابع اختیاری خواهد بود.

مثالی از نمایش نام با استفاده از تابع

1fun main(args: Array<String>) {
2    println(getName("John", "Doe"))
3}
4
5fun getName(firstName: String, lastName: String): String = "$firstName $lastName"

خروجی کد فوق به صورت زیر است:

John Doe

در کد فوق، تابع ()getName دو آرگومان String می‌گیرد و یک مقدار از نوع String بازگشت می‌دهد. در صورتی که تابع همانند مثال فوق، یک عبارت منفرد را بازگشت می‌دهد، الزامی برای استفاده از آکولاد {} برای بدنه تابع پس از علامت = وجود ندارد.

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

fun getName(firstName: String, lastName: String): String = "$firstName $lastName"

با شیوه زیر جایگزین کرد:

fun getName(firstName: String, lastName: String) = "$firstName $lastName"

در این بخش از مقاله آموزش کاتلین توضیح مختصری در خصوص تابع‌های کاتلین و ماهیت و طرز کار آن‌ها ارائه کردیم. در بخش‌های بعدی در این خصوص بیشتر صحبت خواهیم کرد.

فراخوانی تابع به شیوه میانوندی

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

زمانی که از عملگرهای || و && استفاده می‌کنیم، کامپایلر به ترتیب به دنبال تابع‌های or و and می‌گردد و آن‌ها را در پس‌زمینه فراخوانی می‌کند. این دو تابع از نمادگذاری میانوندی پشتیبانی می‌کنند.

مثالی از تابع or و and در کاتلین

1fun main(args: Array<String>) {
2    val a = true
3    val b = false
4    var result: Boolean
5
6    result = a or b // a.or(b)
7    println("result = $result")
8
9    result = a and b // a.and(b)
10    println("result = $result")
11}

خروجی برنامه فوق به صورت زیر است:

result = true

result = false

در برنامه فوق، از عبارت a or b به جای a.or(b) و از عبارت a and b به جای a.and(b) استفاده شده است. دلیل این که امکان چنین کاری وجود دارد، این است که این دو تابع از نمادگذاری میانوندی پشتیبانی می‌کنند.

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

در صورتی که تابعی همه شرایط زیر را داشته باشد، می‌توانیم آن را با استفاده از نمادگذاری میانوندی فراخوانی کنیم:

  • ابن تابع یک تابع عضو (یا تابع بسط) باشد.
  • این تابع تنها یک پارامتر داشته باشد.
  • این تابع با کلیدواژه infix نشانه‌گذاری شده باشد.

مثالی از تابع تعریف ‌شده‌ کاربر با نمادگذاری میانوندی

1class Structure() {
2
3    infix fun createPyramid(rows: Int) {
4        var k = 0
5        for (i in 1..rows) {
6            k = 0
7            for (space in 1..rows-i) {
8                print("  ")
9            }
10            while (k != 2*i-1) {
11                print("* ")
12                ++k
13            }
14            println()
15        }
16    }
17}
18
19fun main(args: Array<String>) {
20    val p = Structure()
21    p createPyramid 4       // p.createPyramid(4)
22}

خروجی برنامه فوق به صورت زیر است:

      *
    * * *
  * * * * *
* * * * * * *

در برنامه فوق، ()createPyramid یک تابع میانوندی است که یک ساختار هرمی ایجاد می‌کند. این تابع یک تابع عضو کلاس Structure است که تنها یک پارامتر از نوع Int می‌گیرد و با کلیدواژه infix آغاز می‌شود.

تعداد ردیف‌های هرم به آرگومان‌های ارسالی به تابع بستگی دارد.

آرگومان‌های پیش‌فرض و نام‌دار در کاتلین

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

آرگومان پیش‌فرض در کاتلین

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

طرز کار آرگومان پیش‌فرض در کاتلین چگونه است؟

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

حالت اول: همه آرگومان‌ها ارسال شوند

آموزش کاتلین

تابع ()foo دو آرگومان می‌گیرد. این آرگومان‌ها دارای مقادیر پیش‌فرض هستند. با این حال در برنامه فوق، تابع ()foo با ارسال هر دو آرگومان فراخوانی شده است. از این رو آرگومان‌های پیش‌فرض مورد استفاده قرار نگرفته‌اند. درون تابع ()foo مقادیر letter و number به ترتیب برابر با 'x' و 2 هستند.

حالت دوم: بعضی آرگومان‌ها به تابع ارسال نشوند

آموزش کاتلین

در این حالت فرض می‌کنیم تنها آرگومان اول به تابع ()foo ارسال شده باشد. در این صورت آرگومان اول از مقدار ارسالی به تابع استفاده می‌کند. با این حال آرگومان دوم، یعنی number مقدار پیش‌فرض را می‌گیرد، زیرا آرگومان دوم در طی فراخوانی تابع ارسال نشده است. درون تابع ()foo مقادیر letter و number به ترتیب برابر با 'y' و 15 هستند.

حالت سوم: هیچ آرگومانی ارسال نشود

آموزش کاتلین

در این حالت تابع ()foo بدون ارسال هیچ آرگومانی فراخوانی می‌شود. از این رو در هر دو مورد از مقادیر پیش‌فرض استفاده می‌شود. بدین ترتیب درون تابع ()foo مقادیر letter و number به ترتیب برابر با 'a' و 15 هستند.

مثالی از آرگومان پیش‌فرض در کاتلین

1fun displayBorder(character: Char = '=', length: Int = 15) {
2    for (i in 1..length) {
3        print(character)
4    }
5}
6
7fun main(args: Array<String>) {
8    println("Output when no argument is passed:")
9    displayBorder()
10
11    println("\n\n'*' is used as a first argument.")
12    println("Output when first argument is passed:")
13    displayBorder('*')
14
15    println("\n\n'*' is used as a first argument.")
16    println("5 is used as a second argument.")
17    println("Output when both arguments are passed:")
18    displayBorder('*', 5)
19
20}

خروجی برنامه فوق به صورت زیر است:

Output when no argument is passed:
===============

'*' is used as a first argument.
Output when first argument is passed:
***************
'*' is used as a first argument.
5 is used as a second argument.
Output when both arguments are passed:
*****

آرگومان نام‌دار در کاتلین

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

1fun displayBorder(character: Char = '=', length: Int = 15) {
2    for (i in 1..length) {
3        print(character)
4    }
5}
6
7fun main(args: Array<String>) {
8    displayBorder(5)
9}

در برنامه فوق تلاش می‌کنیم تا آرگومان دوم را به تابع ()displayBorder ارسال کنیم و از آرگومان پیش‌فرض برای آرگومان اول استفاده می‌کنیم. با این حال، این کد تولید خطا خواهد کرد. دلیل این امر آن است که کامپایلر فکر می‌کند که ما تلاش می‌کنیم مقدار 5 (با نوع Int) را به یک کاراکتر (از نوع Char) ارسال کنیم. برای حل این مشکل باید از آرگومان‌های نام‌دار استفاده کنیم:

مثالی از آرگومان نام‌دار در کاتلین

1fun displayBorder(character: Char = '=', length: Int = 15) {
2    for (i in 1..length) {
3        print(character)
4    }
5}
6
7fun main(args: Array<String>) {
8    displayBorder(length = 5)
9}

خروجی برنامه فوق به صورت زیر است:

=====

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

آموزش کاتلین

بدین ترتیب آرگومان نخست به نام character از مقدار پیش‌فرض '=' در برنامه استفاده می‌کند.

تابع‌های بازگشتی در کاتلین

در این بخش از مقاله آموزش کاتلین به بررسی روش ایجاد تابع‌های بازگشتی در این زبان برنامه‌نویسی می‌پردازیم. «تابع بازگشتی» (Recursive Function) به تابعی گفته می‌شود که خودش را فراخوانی کند. همچنین با مفهوم تابع «بازگشتی انتهایی» (Tail Recursion)‌ در کاتلین آشنا خواهیم شد.

چنان که اشاره کردیم تابعی که خودش را فراخوانی کند، تابع بازگشتی نامیده می‌شود و این تکنیک به طور کلی به نام «بازگشت» (recursion) خوانده می‌شود. یک مثال فیزیکی از این پدیده را می‌توان در زمان قرار دادن دو آینه در روبروی هم مشاهده کرد. هر شیئی که در میان این دو آینه قرار داشته باشد به صورت بازگشتی در آن‌ها بازتاب می‌یابد.

طرز کار بازگشت در برنامه‌نویسی چگونه است؟

1fun main(args: Array<String>) {
2    ... .. ...
3    recurse()
4    ... .. ...
5}
6
7fun recurse() {
8    ... .. ...
9    recurse()
10    ... .. ...
11}

در کد فوق تابع ()recurse از داخل خود بدنه تابع ()recurse فراخوانی می‌شود. طرز کار آن به صورت زیر است:

آموزش کاتلین

در تصویر فوق می‌بینیم که فراخوانی بازگشتی تا ابد ادامه می‌یابد و موجب بروز بازگشت نامتناهی می‌شود. برای جلوگیری از بازگشت نامتناهی از گزاره if…else (یا رویکردهای مشابه) استفاده می‌شود که در یک شاخه از آن فراخوانی بازگشتی انجام می‌یابد و در شاخه دیگر (حصول شرایط مطلوب) ‌متوقف می‌شود.

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

1fun main(args: Array<String>) {
2    val number = 4
3    val result: Long
4
5    result = factorial(number)
6    println("Factorial of $number = $result")
7}
8
9fun factorial(n: Int): Long {
10    return if (n == 1) n.toLong() else n*factorial(n-1)
11}

خروجی برنامه فوق به صورت زیر است:

Factorial of 4 = 24

طرز کار برنامه فوق چگونه است؟

فراخوانی بازگشتی تابع ()factorial را می‌توان با استفاده از تصویر زیر توضیح داد:

آموزش کاتلین

مراحل اجرای برنامه فوق به صورت زیر است:

factorial(4)// 1st function call. Argument: 4
4*factorial(3)// 2nd function call. Argument: 3
4*(3*factorial(2))// 3rd function call. Argument: 2
4*(3*(2*factorial(1)))// 4th function call. Argument: 1
4*(3*(2*1))
24

بازگشت انتهایی در کاتلین

«بازگشت انتهایی» (Tail Recursion) یک مفهوم عمومی است و یک ویژگی زبان کاتلین محسوب نمی‌شود. برخی زبان‌های برنامه‌نویسی شامل کاتلین از آن برای بهینه‌سازی فراخوانی‌های بازگشتی استفاده می‌کنند، در حالی که برخی زبان‌های دیگر (مانند پایتون) از آن‌ها پشتیبانی نمی‌کنند.

بازگشت انتهایی در کاتلین چیست؟

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

اما در فراخوانی بازگشتی انتهایی، محاسبات ابتدا اجرا می‌شوند و سپس فراخوانی‌های بازگشتی اجرا می‌شوند. به این ترتیب فراخوانی بازگشتی، نتیجه اجرای گام قبلی را به فراخوانی بازگشتی بعدی ارائه می‌کند. این امر موجب می‌شود که فراخوانی بازگشتی معادل اجرای حلقه باشد و از خطر بروز «سرریز پشته» (stack overflow) جلوگیری می‌کند.

شرط بازگشت انتهایی

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

  • مثال اول

تابع زیر برای اجرا به صورت «بازگشت انتهایی» مناسب نیست، زیرا فراخوانی تابع به خودش یعنی خط (n*factorial(n-1، آخرین عملیات آن محسوب نمی‌شود.

1fun factorial(n: Int): Long {
2
3    if (n == 1) {
4        return n.toLong()
5    } else {
6        return n*factorial(n - 1)     
7    }
8}
  • مثال دوم

تابع زیر برای اجرا به صورت بازگشت انتهایی مناسب است، زیرا فراخوانی تابع به خودش یعنی fibonacci(n-1, a+b, a) آخرین عملیات آن است.

1fun fibonacci(n: Int, a: Long, b: Long): Long {
2    return if (n == 0) b else fibonacci(n-1, a+b, a)
3}

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

مثالی از بازگشت انتهایی

1import java.math.BigInteger
2
3fun main(args: Array<String>) {
4    val n = 100
5    val first = BigInteger("0")
6    val second = BigInteger("1")
7
8    println(fibonacci(n, first, second))
9}
10
11tailrec fun fibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger {
12    return if (n == 0) a else fibonacci(n-1, b, a+b)
13}

خروجی کد فوق به صورت زیر است:

354224848179261915075

این برنامه صدمین جمله سری فیبوناچی را محاسبه می‌کند. از آنجا که خروجی ممکن است عدد صحیح بسیار بزرگی باشد، باید از کلاس BigInteger در کتابخانه استاندارد جاوا کمک بگیریم. در مثال فوق، تابع ()fibonacci با مادیفایری به نام tailrec مشخص شده است و از این رو امکان اجرای بازگشت انتهایی را دارد. به این ترتیب کامپایلر فرایند بازگشتی را در این حالت بهینه‌سازی می‌کند.

اگر تلاش کنید جمله 20،000 (یا هر عدد صحیح بزرگ دیگر) سری فیبوناچی را بدون بازگشت انتهایی محاسبه کنید، کامپایلر یک استثنا به صورت java.lang.StackOverflowError ایجاد می‌کند. با این حال، برنامه ما بدون هیچ مشکلی کار می‌کند. دلیل این امر آن است که ما از بازگشت انتهایی استفاده کرده‌ایم که از یک حلقه بهینه بر مبنای نسخه‌ای از بازگشت سنتی استفاده می‌کند.

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

مثال فوق برای محاسبه فاکتوریل یک عدد (مثال اول) را نمی‌توان برای اجرا به صورت بازگشت انتهایی بهینه‌سازی کرد. اما برنامه زیر همین وظیفه را به روش متفاوتی اجرا می‌کند.

1fun main(args: Array<String>) {
2    val number = 5
3    println("Factorial of $number = ${factorial(number)}")
4}
5
6tailrec fun factorial(n: Int, run: Int = 1): Long {
7    return if (n == 1) run.toLong() else factorial(n-1, run*n)
8}

خروجی برنامه فوق به صورت زیر است:

Factorial of 5 = 120

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

کلاس و شیء در کاتلین

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

کاتلین از هر دو پارادایم برنامه‌نویسی تابعی و شیء‌گرا پشتیبانی می‌کند. قابلیت‌های کاتلین امکان نوشتن تابع‌های مرتبه بالا، انواع مختلف تابع‌ها و لامبدا را فراهم می‌سازد و به این ترتیب به گزینه‌ای عالی برای سبک برنامه‌نویسی تابعی تبدیل می‌شود. اما در این بخش بر روی بررسی مفاهیم سبک برنامه‌نویسی شیءگرا متمرکز خواهیم شد.

برنامه‌نویسی شی‌ءگرا

در سبک برنامه‌نویسی شیء‌گرا (OOP) یک مسئله پیچیده با ایجاد شیء‌هایی به مجموعه‌های کوچک‌تر تقسیم می‌شود. این اشیا دو خصوصیت دارند:

  • حالت
  • رفتار

در ادامه برخی مثال‌هایی از اشیا را بررسی می‌کنیم.

  1. لامپ (Lamp) یک شیء است:
    1. این شیء می‌تواند حالت‌های روشن (On) و خاموش (Off) داشته باشد.
    2. این شیء می‌تواند رفتار روشن کردن (turn on) و خاموش کردن (turn off) را بگیرد.
  2. دوچرخه (Bicycle) یک شیء است.
    1. این شیء می‌تواند حالت‌های «دنده کنونی» (current gear)، «دو چرخ» (two wheels)‌، «تعداد دنده» (number of gear) ‌و بسیاری حالت‌های دیگر را داشته باشد.
    2. این شیء می‌تواند رفتار «ترمز کردن» (braking)، «پدال زدن» (changing gears)، «تغییر دادن دنده» (changing gears) و بسیاری رفتارهای دیگر را داشته باشد.

در بخش‌های بعدی این مقاله آموزش کاتلین در خصوص ویژگی‌های مختلف برنامه‌نویسی شیءگرا از قبیل کپسوله‌سازی داده‌ها، وراثت، چندریختی و غیره صحبت خواهیم کرد. در این بخش صرفاً روی مبانی مقدماتی شیءگرایی در کاتلین تمرکز می‌کنیم.

کلاس کاتلین

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

شیوه تعریف کلاس در کاتلین چگونه است؟

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

1class ClassName {
2    // property
3    // member function
4    ... .. ...
5}

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

1class Lamp {
2
3    // property (data member)
4    private var isOn: Boolean = false
5
6    // member function
7    fun turnOn() {
8        isOn = true
9    }
10
11    // member function
12    fun turnOff() {
13        isOn = false
14    }
15}

در مثال فوق یک کلاس با نام Lamp تعریف کرده‌ایم. این کلاس دارای یک مشخصه به نام isOn است که همانند یک متغیر تعریف شده است. همچنین دو تابع عضو به نام‌های ()turnOn و ()turnOff دارد.

یک مشخصه در کلاس‌های کاتلین یا باید مقداردهی شود و یا به صورت «مجرد» (abstract) اعلان شود. در مثال فوق، مشخصه isOn به صورت False مقداردهی شده است. کلاس‌ها، اشیا، مشخصه‌ها، تابع عضو و غیره، همگی دارای مادیفایرهای «نمایانی» (Visibility) هستند. برای نمونه مشخصه isOn خصوصی است. این بدان معنی است که مشخصه isOn تنها از درون کلاس Lamp می‌تواند تغییر یابد.

مادیفایرهای دیگر نمایانی در کاتلین به شرح زیر هستند:

  • Private – صرفاً از درون خود کلاس قابل مشاهده (دسترسی) است.
  • Public – در هر کجا نمایان است.
  • Protected – برای کلاس و زیرکلاس‌های آن نمایان است.
  • Internal – هر کلاینت درون ماژول می‌تواند به آن دسترسی داشته باشد.

در بخش‌های بعدی در مورد مادیفایرهای protected و internal بیشتر صحبت خواهیم کرد. در برنامه فوق، تابع‌های عضو ()turnOn و ()turnOff به صورت عمومی (public) هستند، در حالی که مشخصه isOn خصوصی (private) است.

اشیای کاتلین

در زمان تعریف یک کلاس، صرفاً مشخصه‌های شیء تعریف می‌شوند و هیچ حافظه یا فضای ذخیره‌سازی به آن تخصیص نمی‌یابد. برای دسترسی به تابع‌های عوض درون کلاس باید از روی آن اشیایی ایجاد کرد. در ادامه مثالی از ایجاد یک شیء بر مبنای کلاس Lamp می‌بینید که در بخش قبل تعریف کردیم:

1class Lamp {
2
3    // property (data member)
4    private var isOn: Boolean = false
5
6    // member function
7    fun turnOn() {
8        isOn = true
9    }
10
11    // member function
12    fun turnOff() {
13        isOn = false
14    }
15}
16
17fun main(args: Array<String>) {
18
19    val l1 = Lamp() // create l1 object of Lamp class
20    val l2 = Lamp() // create l2 object of Lamp class
21}

برنامه فوق دو شیء به نام‌های l1 و l2 از روی کلاس Lamp ایجاد می‌کند. مشخصه isOn برای هر دو لامپ l1 و l2 به صورت false است.

شیوه دسترسی به اعضا چگونه است؟

امکان دسترسی به مشخصه‌ها و تابع‌های عضو یک کلاس با استفاده از نماد نقطه (.) وجود دارد. به مثال زیر توجه کنید:

l1.turnOn()

گزاره فوق تابع ()turnOn را روی شیء l1 فرا می‌خواند. به مثال زیر نیز توجه کنید:

l2.isOn = true

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

مثالی از کلاس و شیء کاتلین

1class Lamp {
2
3    // property (data member)
4    private var isOn: Boolean = false
5
6    // member function
7    fun turnOn() {
8        isOn = true
9    }
10
11    // member function
12    fun turnOff() {
13        isOn = false
14    }
15
16    fun displayLightStatus(lamp: String) {
17        if (isOn == true)
18            println("$lamp lamp is on.")
19        else
20            println("$lamp lamp is off.")
21    }
22}
23
24fun main(args: Array<String>) {
25
26    val l1 = Lamp() // create l1 object of Lamp class
27    val l2 = Lamp() // create l2 object of Lamp class
28
29    l1.turnOn()
30    l2.turnOff()
31
32    l1.displayLightStatus("l1")
33    l2.displayLightStatus("l2")
34}

خروجی برنامه فوق به صورت زیر است:

l1 Lamp is on.
l2 Lamp is off.

در این بخش در خصوص برنامه فوق برخی توضیح‌ها را ارائه می‌کنیم:

  • ابتدا کلاس Lamp ایجاد می‌شود.
  • این کلاس دارای مشخصه isOn و سه تابع عضو به صورت ()turnOn() ،turnOff و ()displayLightStatus است.
  • دو شیء l1 و l2 مربوط به کلاس Lamp در تابع ()main ایجاد شده‌اند.
  • در ادامه تابع ()turnOn با استفاده از شیء l1 به صورت ()l1.turnOn فراخوانی می‌شود. این متد موجب می‌شود که متغیر وهله‌ای isOn مربوط به شیء l1 به صورت true تنظیم شود.
  • سپس تابع ()turnOff با استفاده از شیء l2 به صورت ()l2.turnOff فراخوانی می‌شود. این متد نیز موجب می‌شود که متد وهله‌ای isOff مربوط به شیء l2 به صورت false تنظیم شود.
  • در نهایت تابع ()displayLightStatus برای هر دو شیء l1 و l2 فراخوانی می‌شود و پیام مناسبی بسته به این که مشخصه isOn مقدار true یا false داشته باشد، تنظیم می‌کند.

توجه کنید که مشخصه isOn درون کلاس به صورت false مقداردهی می‌شود. زمانی که یک شیء از کلاس ایجاد می‌شود، مشخصه isOn مربوط به آن شیء به صورت خودکار روی false تنظیم می‌شود. بنابراین لزومی به فراخوانی ()turnOff برای تنظیم مقدار مشخصه isOn به مقدار false وجود نخواهد داشت.

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

1class Lamp {
2
3    // property (data member)
4    private var isOn: Boolean = false
5
6    // member function
7    fun turnOn() {
8        isOn = true
9    }
10
11    // member function
12    fun turnOff() {
13        isOn = false
14    }
15
16    fun displayLightStatus() {
17        if (isOn == true)
18            println("lamp is on.")
19        else
20            println("lamp is off.")
21    }
22}
23
24fun main(args: Array<String>) {
25
26    val lamp = Lamp()
27    lamp.displayLightStatus()
28}

خروجی برنامه فوق چنین است:

lamp is off.

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

سازنده در کاتلین

در این بخش از مقاله آموزش کاتلین به بررسی «سازنده‌ها» (constructors) در کاتلین می‌پردازیم. در این مسیر هر دو نوع سازنده اولیه و ثانویه را معرفی کرده و همچنین بلوک‌های مقداردهی را به کمک مثال‌هایی مورد بررسی قرار می‌دهیم.

سازنده یا constructor یک روش منسجم برای مقداردهی مشخصه‌های کلاس محسوب می‌شود. سازنده یک تابع عضو خاص کلاس است که در زمان مقداردهی (ایجاد) شیء فراخوانی می‌شود. با این حال، طرز کار سازنده‌ها در کاتلین نسبت به جاوا و زبان‌های دیگر برنامه‌نویسی کمی متفاوت است.

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

  • سازنده اولیه: روش منسجمی برای مقداردهی یک کلاس است.
  • سازنده ثانویه: امکان تعریف منطق اضافی برای مقداردهی را فراهم می‌سازد.

سازنده اولیه

سازنده اولیه بخشی از هدر کلاس محسوب می‌شود. به مثال زیر توجه کنید:

1class Person(val firstName: String, var age: Int) {
2    // class body
3}

بلوک کدی که درون پرانتز احاطه شده، سازنده اولیه است. ساختار کلی آن به صورت زیر است:

(val firstName: String, var age: Int)

سازنده دو مشخصه اعلان می‌کند که یکی firstName است که یک مشخصه فقط-خواندنی است، زیرا با کلیدواژه val اعلان شده است و دیگری مشخصه age است که امکان خواندن-نوشتن دارد، زیرا با کلیدواژه var اعلان شده است.

مثالی از سازنده اولیه

1fun main(args: Array<String>) {
2
3    val person1 = Person("Joe", 25)
4
5    println("First Name = ${person1.firstName}")
6    println("Age = ${person1.age}")
7}
8
9class Person(val firstName: String, var age: Int) {
10
11}

خروجی برنامه فوق چنین است:

First Name = Joe
Age = 25

زمانی که شیء کلاس Person ایجاد شود، مقادیر "Joe" و 25 طوری به آن ارسال می‌شوند که گویی Person یک تابع است. این امر موجب مقداردهی مشخصه‌های firstName و age به ترتیب با مقادیر "Joe" و 25 می‌شود. روش‌های دیگری نیز برای استفاده از سازنده‌های اولیه وجود دارد که در بخش بعدی توضیح می‌دهیم.

سازنده اولیه و بلوک‌های مقداردهی

سازنده اولیه دارای یک ساختار مقید است و نمی‌تواند شامل هیچ کدی باشد. برای نوشتن کد مقداردهی اولیه (و نه فقط کدی که مشخصه‌ها را مقداردهی کند) باید از یک بلوک مقداردهی استفاده کنیم. کلیدواژه ابتدایی این بلوک به صورت init است. در ادامه یک بلوک مقداردهی اولیه به ابتدای مثال قبلی اضافه می‌کنیم:

1fun main(args: Array<String>) {
2    val person1 = Person("joe", 25)
3}
4
5class Person(fName: String, personAge: Int) {
6    val firstName: String
7    var age: Int
8
9    // initializer block
10    init {
11        firstName = fName.capitalize()
12        age = personAge
13
14        println("First Name = $firstName")
15        println("Age = $age")
16    }
17}

خروجی برنامه فوق به صورت زیر است:

First Name = Joe

Age = 25

در مثال فوق، پارامترهای fName و personage درون پرانتزها، مقادیر "Joe" و 25 به ترتیب در زمان ایجاد شیء person1 دریافت می‌شود. با این حال fName و personage بدون استفاده از var و val استفاده شده‌اند و مشخصه‌های کلاس Person هستند.

کلاس Person دو مشخصه firstName و age را اعلان کرده است. زمانی که شیء person1 ایجاد می‌شود، کد درون بلوک مقداردهی اجرا می‌شود. بلوک مقداردهی نه تنها مشخصه‌ها را مقداردهی اولیه می‌کند بلکه آن‌ها را پرینت نیز می‌کند.

برای اجرای این وظیفه یک روش دیگر وجود دارد:

1fun main(args: Array<String>) {
2    val person1 = Person("joe", 25)
3}
4
5class Person(fName: String, personAge: Int) {
6    val firstName = fName.capitalize()
7    var age = personAge
8
9    // initializer block
10    init {
11        println("First Name = $firstName")
12        println("Age = $age")
13    }
14}

برای ایجاد تمایز بین پارامتر سازنده و مشخصه باید از نام‌ها متفاوتی استفاده کنیم. برای مثال ما از نام‌های fName و firstName و همچنین personAge و age بهره می‌گیریم. به طور معمول از نام‌هایی مانند ‎_firstName و ‎_age به جای نام‌های کاملاً متفاوت برای پارامترهای سازنده استفاده می‌کنیم. به مثال زیر توجه کنید:

1class Person(_firstName: String, _age: Int) {
2    val firstName = _firstName.capitalize()
3    var age = _age
4
5    // initializer block
6    init {
7        ... .. ...
8    }
9}

مقدار پیش‌فرض در سازنده اولیه

برای پارامترهای سازنده می‌توان مقادیر پیش‌فرض ارائه کرد. به مثال زیر توجه کنید:

1fun main(args: Array<String>) {
2
3    println("person1 is instantiated")
4    val person1 = Person("joe", 25)
5
6    println("person2 is instantiated")
7    val person2 = Person("Jack")
8
9    println("person3 is instantiated")
10    val person3 = Person()
11}
12
13class Person(_firstName: String = "UNKNOWN", _age: Int = 0) {
14    val firstName = _firstName.capitalize()
15    var age = _age
16
17    // initializer block
18    init {
19        println("First Name = $firstName")
20        println("Age = $age\n")
21    }
22}

خروجی برنامه فوق به صورت زیر است:

First Name = Joe
Age = 25

person2 is instantiated
First Name = Jack
Age = 0

person3 is instantiated
First Name = UNKNOWN
Age = 0

سازنده ثانویه کاتلین

کلاس در کاتلین می‌تواند شامل یک یا چند سازنده ثانویه نیز باشد. این سازنده‌های ثانویه با استفاده از کلیدواژه constructor ساخته می‌شوند.

سازنده‌های ثانویه در کاتلین رایج نیستند. رایج‌ترین کاربرد سازنده ثانویه در زمانی است که لازم است یک کلاس را بسط دهیم که چند سازنده دارد و کلاس را به روش‌های متفاوتی بسط می‌دهد. این موضوع با بحث وراثت در کاتلین (+) مرتبط است که در ادامه بیشتر بررسی خواهیم کرد. روش ساخت سازنده ثانویه در کاتلین به شرح زیر است:

1class Log {
2    constructor(data: String) {
3        // some code
4    }
5    constructor(data: String, numberOfData: Int) {
6        // some code
7    }
8}

کلاس log و سازنده ثانویه دارد، اما هیچ سازنده اولیه ندارد. این کلاس را می‌توان به صورت زیر بسط داد:

1class Log {
2    constructor(data: String) {
3        // code
4    }
5    constructor(data: String, numberOfData: Int) {
6        // code
7    }
8}
9
10class AuthLog: Log {
11    constructor(data: String): super(data) {
12        // code
13    }
14    constructor(data: String, numberOfData: Int): super(data, numberOfData) {
15        // code
16    }
17}

در این مثال، سازنده‌های کلاس مشتق‌‌شده به نام AuthLog، سازنده متناظر کلاس مبنا به نام Log را فراخوانی می‌کنند. به این منظور از ()super استفاده می‌کنیم.

Kotlin

در کاتلین می‌توان یک سازنده را از سازنده دیگر همان کلاس نیز با استفاده از ()this فراخوانی کرد.

1class AuthLog: Log {
2    constructor(data: String): this(data, 10) {
3        // code
4    }
5    constructor(data: String, numberOfData: Int): super(data, numberOfData) {
6        // code
7    }
8}

Kotlin

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

1fun main(args: Array<String>) {
2
3    val p1 = AuthLog("Bad Password")
4}
5
6open class Log {
7    var data: String = ""
8    var numberOfData = 0
9    constructor(_data: String) {
10
11    }
12    constructor(_data: String, _numberOfData: Int) {
13        data = _data
14        numberOfData = _numberOfData
15        println("$data: $numberOfData times")
16    }
17}
18
19class AuthLog: Log {
20    constructor(_data: String): this("From AuthLog -> " + _data, 10) {
21    }
22
23    constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) {
24    }
25}

خروجی برنامه فوق به صورت زیر است:

From AuthLog -> Bad Password: 10 times

نکته: سازنده ثانویه باید کلاس مبنا را مقداردهی کند یا در صورتی که مانند مثال فوق، کلاس مورد نظر سازنده اولیه نداشته باشد، سازنده دیگری را «نمایندگی» (delegate) کند.

Getter-ها و Setter-ها در کاتلین

در این بخش از مقاله آموزش کاتلین با شیوه استفاده از Getter-ها و setter-ها در کاتلین آشنا خواهیم شد. پیش از آن که وارد این بحث شویم، باید مطمئن شوید که با مفاهیم کلاس و شیء آشنا هستید. در برنامه‌نویسی getter-ها برای دریافت مقدار یک مشخصه استفاده می‌شوند. به طور مشابه setter-ها نیز برای تنظیم مقادیر مشخصه‌‌ها مورد استفاده قرار می‌گیرند.

ایجاد getter-ها و setter-ها در کاتلین به صورت اختیاری صورت می‌گیرد و در صورتی که آن‌ها را خودتان ایجاد نکنید، به صورت خودکار ایجاد می‌شوند.

طرز کار getter و setter چگونه است؟

به کد زیر در کاتلین توجه کنید:

1class Person {
2    var name: String = "defaultValue"
3}

کد فوق معادل کد زیر است:

1class Person {
2    var name: String = "defaultValue"
3
4    // getter
5    get() = field
6
7    // setter
8    set(value) {
9        field = value
10    }
11}

زمانی که یک وهله شیء از کلاس Person ایجاد می‌کنید و مشخصه name را مقداردهی می‌کنید، این مقدار به پارامتر setter مربوط به value ارسال می‌شود و مقدار field به صورت value تعیین می‌شود.

1val p = Person()
2p.name = "jack"

اکنون زمانی که به مشخصه name شیء دسترسی پیدا کنید، field را به دست می‌آورید، زیرا کدی مانند get() = field وجود دارد:

1println("${p.name}")

در ادامه یک مثال عملی را بررسی می‌کنیم:

1fun main(args: Array<String>) {
2
3    val p = Person()
4    p.name = "jack"
5    println("${p.name}")
6}
7
8class Person {
9    var name: String = "defaultValue"
10
11    get() = field
12
13    set(value) {
14        field = value
15    }
16}

زمانی که برنامه فوق را اجرا کنید، خروجی زیر به دست می‌آید:

Jack

این طرز کار getter و setter به صورت پیش‌فرض است. با این حال می‌توان مقدار مشخصه را با استفاده از getter و setter تغییر نیز داد.

مثالی از تغییر دادن مقدار و مشخصه

1fun main(args: Array<String>) {
2
3    val maria = Girl()
4    maria.actualAge = 15
5    maria.age = 15
6    println("Maria: actual age = ${maria.actualAge}")
7    println("Maria: pretended age = ${maria.age}")
8
9    val angela = Girl()
10    angela.actualAge = 35
11    angela.age = 35
12    println("Angela: actual age = ${angela.actualAge}")
13    println("Angela: pretended age = ${angela.age}")
14}
15
16class Girl {
17    var age: Int = 0
18    get() = field
19    set(value) {
20        field = if (value < 18)
21            18
22        else if (value >= 18 && value <= 30)
23            value
24        else
25            value-3
26    }
27
28    var actualAge: Int = 0
29}

خروجی برنامه فوق به صورت زیر است:

Maria: actual age = 15
Maria: pretended age = 18
Angela: actual age = 35
Angela: pretended age = 32

در کد فوق مشخصه actualAge مطابق انتظار ما عمل می‌کند. با این حال منطقی اضافی نیز وجود دارد که از setter برای اصلاح مقدار مشخصه age استفاده می‌کند.

وراثت در کاتلین

در این بخش از مقاله آموزش کاتلین به بررسی مفهوم وراثت در این زبان برنامه‌نویسی می‌پردازیم. به بیان دقیق‌تر در این بخش با ماهیت وراثت به عنوان یکی از ارکان اساسی برنامه‌نویسی شیءگرا آشنا شده و با بررسی مثال‌های عملی شیوه پیاده‌سازی آن را در زبان کاتلین بررسی خواهیم کرد.

وراثت یکی از قابلیت‌های کلیدی برنامه‌نویسی شیءگرا محسوب می‌شود و به کاربران امکان می‌دهد که یک کلاس جدید (کلاس مشتق‌شده) را از کلاس موجود (کلاس مبنا) بسازند. کلاس مشتق‌شده همه قابلیت‌های کلاس مبنا را به ارث می‌برد و می‌تواند برخی قابلیت‌های خاص اضافی نیز برای خود داشته باشد.

چرا باید از وراثت استفاده کنیم؟

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

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

Kotlin

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

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

در صورتی که یک کلاس Person داشته باشید که دارای برخی قابلیت‌های اولیه مانند راه رفتن، صحبت کردن، خوردن، خوابیدن باشد و در ادامه برخی مهارت‌های خاص را بنا به نوع شخصیت به هر کدام اضافه کنید، روند کار تا حدود زیادی بهینه می‌شود. این وضعیت به نام وراثت خوانده می‌شود.

Kotlin

بدین ترتیب با بهره‌گیری از وراثت، دیگر لازم نیست کد یکسانی را برای متدهای ()walk() ،talk و ()eat در هر کلاس بنویسید. کافی است این قابلیت‌ها را ارث‌بری کنید.

بنابراین در مورد MathTeacher که یک کلاس مشتق‌شده است، همه قابلیت‌های Person را که کلاس مبنا است به ارث می‌بریم و یک قابلیت جدید به صورت ()teachMath اضافه می‌کنیم. به طور مشابه، در مورد کلاس Footballer، همه قابلیت‌ها را از کلاس Person ارث‌بری می‌کنیم و یک قابلیت جدید به نام ()playFootball نیز اضافه می‌کنیم.

این کار موجب می‌شود که کد تمیزتر شده و قابلیت درک و بسط‌پذیری بالاتری پیدا کند. باید در خاطر داشته باشید که وقتی با وراثت کار می‌کنیم، هر کلاس مشتق‌شده باید یک شرط (is a) ‌داشته باشد که تعیین کنید آیا یک کلاس مبنا است یا نه. در این مثال، MathTeacher یک کلاس Person است. همچنین Footballer یک کلاس مبنا است. اما Businessman یک کلاس مشتق‌شده از Business نیست.

وراثت در کاتلین

اگر بخواهیم مباحث فوق را در کاتلین پیاده‌سازی کنیم به کد زیر می‌رسیم:

1open class Person(age: Int) {
2    // code for eating, talking, walking
3}
4
5class MathTeacher(age: Int): Person(age) {
6    // other features of math teacher
7}
8
9class Footballer(age: Int): Person(age) {
10    // other features of footballer
11}
12
13class Businessman(age: Int): Person(age) {
14    // other features of businessman
15}

در کد فوق Person یک کلاس مبنا است و کلاس‌های MathTeacher ،Footballer و Businessman از کلاس Person مشتق شده‌اند. توجه کنید که کلیدواژه open که پیش از آکولاد در Person آمده، بسیار مهم است.

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

مثالی از وراثت در کاتلین

1open class Person(age: Int, name: String) {
2    init {
3        println("My name is $name.")
4        println("My age is $age")
5    }
6}
7
8class MathTeacher(age: Int, name: String): Person(age, name) {
9
10    fun teachMaths() {
11        println("I teach in primary school.")
12    }
13}
14
15class Footballer(age: Int, name: String): Person(age, name) {
16    fun playFootball() {
17        println("I play for LA Galaxy."</