آشنایی با کاتلین بخش سوم (آموزش آبجکت ها و کلاس ها)
فهرست مطالب
آخرین به روزرسانی در 21/10/2022
در بخش سوم آموزش کاتلین قصد آموزش آبجکت ها و کلاس ها در کاتلین را داریم.
در بخش قبلی آموزش زبان کاتلین ما مباحث پایه و اصول کلی این زبان از کلمات کلیدی و متغیرها تا توابع پرداختیم.
اما در این بخش قصد داریم به صورت اختصاصی تری توضیحاتی را ارائه دهیم که حول محور آموزش آبجکت ها و کلاس ها در کاتلین می چرخد.
پس تا پایان با ما همراه باشید تا بخش سوم از آموزش را نیز به پایان برسانیم.
لازم به ذکر است پیش نیاز این بخش از دوره ی آموزش مطالعه و تسلط بر بخش دوم آموزش زبان کاتلین می باشد.
کلاس ها در کاتلین
یک کلاس یک طرح اولیه برای آبجکت ها است که یک الگو را برای ایجاد آبجکت های مورد نیاز تعریف می کند.
کلاس ها بلوک های اصلی هر زبان برنامه نویسی شی گرا هستند.
یک کلاس Kotlin با استفاده از کلمه کلیدی class تعریف می شود.
دستور زیر برای ایجاد یک کلاس Kotlin آمده است :
یک اعلان کلاس Kotlin شبیه به Java Programmig است که از یک هدر کلاس و یک بادی کلاس احاطه شده توسط پرانتزها تشکیل شده است.
class ClassName { // Class Header
//
// Variables or data members
// Member functions or Methods
//
...
...
بهطور پیشفرض، کلاسهای کاتلین public هستند و میتوانیم با استفاده از modifiers مختلفی که در Visibility Modifiers یاد میگیریم، دید اعضای کلاس را کنترل کنیم.
آبجکت ها در کاتلین
اشیاء یا همان آبجکت ها از کلاس Kotlin ایجاد می شوند و خصوصیات و رفتارهای مشترک تعریف شده توسط یک کلاس را به ترتیب در قالب data members (properties) و توابع عضو (behaviours) به اشتراک می گذارند.
سینتکس برای اعلان یک شی از یک کلاس به صورت زیر است:
var varName = ClassName()
ما میتوانیم به ویژگیها و متدهای یک کلاس با استفاده از عملگر نقطه . مطابق حالت زیر اشاره کنیم :
var varName = ClassName()
varName.property =
varName.functionName()
در زیر مثالی وجود دارد که در آن یک کلاس Kotlin و شیء آن ایجاد می کنیم که از طریق آن به data members مختلف آن کلاس دسترسی خواهیم داشت.
class myClass {
// Property (data member)
private var name: String = "manataz.com"
// Member function
fun printMe() {
print("The best Learning website - " + name)
}
}
fun main(args: Array) {
val obj = myClass() // Create object obj of myClass class
obj.printMe() // Call a member function using object
}
قطعه کد بالا خروجی زیر را در مرورگر به دست می دهد، جایی که ما متد printMe() myClass را با کمک شیء خاص خود obj فراخوانی می کنیم.
The best Learning website – manataz.com
کلاس های تو در تو در کاتلین (nested class)
طبق تعریف، زمانی که یک کلاس در داخل کلاس دیگری ایجاد شده است، آن را به عنوان یک کلاس تودرتو (nested class) می نامند.
کلاس تودرتوی Kotlin به طور پیش فرض ثابت است.
اما با کمک عملگر نقطه . در همان زمان ما نمی توانیم به اعضای کلاس خارجی در داخل یک کلاس تودرتو دسترسی داشته باشیم.
در زیر دستور ساده برای ایجاد یک کلاس تودرتو آمده است:
class OuterClass{
// Members of Outer Class
class NestedClass{
// Members of Nested Class
}
}
اکنون می توانیم یک شی از کلاس تودرتو به صورت زیر ایجاد کنیم:
val object = OuterClass.NestedClass()
در زیر مثالی آورده شده است تا نشان دهد کاتلین چگونه یک کلاس تودرتو را تفسیر می کند.
fun main(args: Array) {
val obj = Outer.Nested()
print(obj.foo())
}
class Outer {
class Nested {
fun foo() = "Welcome to The manataz.com"
}
}
کلاس داخلی کاتلین (Inner Class)
هنگامی که یک کلاس تودرتو با یک کلمه کلیدی inner مشخص می شود، آنگاه به عنوان یک کلاس داخلی (Inner class) فراخوانی می شود.
یک Inner class توسط data member کلاس خارجی قابل دسترسی است.
برخلاف کلاس تودرتو، Inner class می تواند به اعضای کلاس بیرونی دسترسی داشته باشد.
ما نمی توانیم مستقیماً یک شی از کلاس داخلی ایجاد کنیم، اما می توان آن را با استفاده از شی کلاس خارجی ایجاد کرد.
در زیر سینتکس ساده برای ایجاد یک کلاس داخلی آمده است:
class OuterClass{
// Members of Outer Class
class inner InnerClass{
// Members of Inner Class
}
}
اکنون می توانیم یک شی از کلاس داخلی به صورت زیر ایجاد کنیم:
val outerObj = OuterClass()
val innerObj = outerObj.InnerClass()
مثال زیر برای نشان دادن نحوه تفسیر یک Inner class در کاتلین است.
fun main(args: Array) {
val obj = Outer().Inner()
print(obj.foo())
}
class Outer {
private val welcomeMessage: String = "Welcome to the manataz.com"
inner class Inner {
fun foo() = welcomeMessage
}
}
سازنده ها در کاتلین
سازنده Kotlin یک تابع عضو ویژه در یک کلاس است که هنگام نمونه سازی یک شی فراخوانی می شود.
هر زمان که یک آبجکت ایجاد می شود، سازنده تعریف شده به طور خودکار فراخوانی می شود که برای مقداردهی اولیه properties کلاس استفاده می شود.
هر کلاس Kotlin نیاز به یک سازنده دارد و اگر آن را تعریف نکنیم، کامپایلر یک سازنده پیش فرض ایجاد می کند.
یک کلاس Kotlin می تواند دو نوع سازنده زیر داشته باشد:
- Primary Constructor
- Second Constructors
یک کلاس Kotlin می تواند یک Primary Constructor و یک یا چند Second Constructors اضافی داشته باشد.
سازنده اولیه Kotlin کلاس را مقداردهی اولیه می کند، در حالی که Second Constructors به گنجاندن logic اضافی در هنگام مقداردهی اولیه کلاس کمک می کند.
Primary Constructor
primary constructor (سازنده اولیه) را می توان در class header level همانطور که در مثال زیر نشان داده شده است اعلان کرد.
class Person constructor(val firstName: String, val age: Int) {
// class body
}
در صورتی که هیچ access modifiers مانند public ، private یا protected مشخص نشده باشد، می توان کلمه کلیدی constructor را حذف کرد.
در این مثال، properties را از طریق کلمه کلیدی val اعلام کرده ایم تا آنها را فقط خواندنی کنیم.
این properties را می توان با استفاده از کلمه کلیدی var تعریف کرد اگر بخواهید مقادیر آنها را در زمان بعدی تغییر دهید.
Initializer Block
primary constructor نمی تواند حاوی هیچ کدی باشد.
Initialization code را میتوان در initializer blocks با پیشوند کلیدواژه init قرار داد.
ممکن است بیش از یک initializer blocks وجود داشته باشد و در طول مقداردهی اولیه یک نمونه، initializer blocks به همان ترتیبی که در بادی کلاس ظاهر می شوند، اجرا می شوند و با مقداردهی property initializers در هم می آمیزند :
در زیر مثالی با استفاده از initializer blocks آورده شده است :
class Person (val _name: String, val _age: Int) {
// Member Variables
var name: String
var age: Int
// Initializer Block
init {
this.name = _name
this.age = _age
println("Name = $name")
println("Age = $age")
}
}
fun main(args: Array) {
val person = Person("mehrsa", 23)
}
خروجی :
Name = mehrsa
Age = 23
مقادیر پیشفرض
Kotlin اجازه می دهد تا پارامترهای سازنده را با مقادیر پیش فرض مقداردهی اولیه کنید.
در زیر یک مثال کار برای همین است:
class Person (val _name: String, val _age: Int=20) {
// Member Variables
var name: String
var age: Int
// Initializer Block
init {
this.name = _name
this.age = _age
println("Name = $name")
println("Age = $age")
}
}
fun main(args: Array) {
val zara = Person("Zara")
val nuha = Person("Nuha", 11)
}
خروجی :
Name = Zara
Age = 20
Name = Nuha
Age = 11
Secondary Constructor
همانطور که قبلا ذکر شد، کاتلین اجازه می دهد تا یک یا چند Secondary Constructor برای کلاس خود ایجاد کنید.
این Secondary Constructor با استفاده از کلمه کلیدی Constructor ایجاد می شود.
هر زمان که بخواهید بیش از یک سازنده در کاتلین ایجاد کنید یا هر زمان که بخواهید منطق بیشتری را در سازنده اصلی بگنجانید ؛ نمی توانید این کار را انجام دهید زیرا سازنده اولیه ممکن است توسط کلاس دیگری فراخوانی شود.
به مثال زیر نگاهی بیندازید، در اینجا ما یک Secondary Constructor برای پیاده سازی مثال بالا ایجاد کرده ایم:
class Person{
// Member Variables
var name: String
var age: Int
// Initializer Block
init {
println("Initializer Block")
}
// Secondary Constructor
constructor ( _name: String, _age: Int) {
this.name = _name
this.age = _age
println("Name = $name")
println("Age = $age")
}
}
fun main(args: Array) {
val zara = Person("Zara", 20)
}
خروجی :
Initializer Block
Name = Zara
Age = 20
Secondary Constructor اجازه استفاده از val یا var را با پارامترهای Secondary Constructor نمی دهد.
حالا بیایید یک مثال را با دو Secondary Constructor ببینیم:
class Person{
// Member Variables
var name: String
var age: Int
var salary:Double
// First Secondary Constructor
constructor ( _name: String, _age: Int) {
this.name = _name
this.age = _age
this.salary = 0.00
println("Name = $name")
println("Age = $age")
}
// Second Secondary Constructor
constructor ( _name: String, _age: Int, _salary: Double) {
this.name = _name
this.age = _age
this.salary = _salary
println("Name = $name")
println("Age = $age")
println("Salary = $salary")
}
}
fun main(args: Array) {
val nuha = Person("Nuha", 12)
val zara = Person("Zara", 20, 2000.00)
}
خروجی :
Name = Nuha
Age = 12
Name = Zara
Age = 20
Salary = 2000.0
وراثت
وراثت را می توان به عنوان فرآیندی تعریف کرد که در آن یک کلاس، متد ها و ویژگی های (properties) کلاس دیگر را به دست می آورد.
با استفاده از وراثت، اطلاعات به ترتیب سلسله مراتبی قابل مدیریت می شوند.
کلاسی که اعضای کلاس دیگر را به ارث می برد به عنوان زیر کلاس (کلاس مشتق شده یا کلاس فرزند) و کلاسی که اعضای آن در حال ارث بردن هستند به عنوان سوپرکلاس (کلاس پایه یا کلاس والد) شناخته می شوند.
وراثت یکی از ویژگی های کلیدی برنامه نویسی شی گرا است که به کاربر اجازه می دهد یک کلاس جدید از یک کلاس موجود ایجاد کند.
در وراثت ما می توانیم همه ویژگی ها را از کلاس پایه به ارث ببریم و همچنین می توانیم ویژگی های اضافی خود را داشته باشیم.
همه کلاسها در کاتلین دارای یک سوپرکلاس مشترک به نام Any هستند که سوپرکلاس پیشفرض برای کلاسهایی است که هیچ سوپرتایپ اعلامشدهای ندارد:
class Example // Implicitly inherits from Any
سوپرکلاس Any دارای سه متد: () quals ()، hashCode و () toString می باشد.
بنابراین، این متد ها برای تمام کلاس های Kotlin تعریف شده اند.
همه چیز در Kotlin به طور پیشفرض final است، از این رو، باید از کلمه کلیدی open در جلوی اعلان کلاس استفاده کنیم تا آن را برای کلاسهای دیگر ارثی کنیم.
کاتلین از عملگر “:” برای به ارث بردن یک کلاس استفاده می کند.
مثال :
open class ABC {
fun think () {
println("Hey!! i am thiking ")
}
}
class BCD: ABC(){ // inheritence happend using default constructor
}
fun main(args: Array) {
var a = BCD()
a.think()
}
خروجی :
Hey!! i am thiking
Overriding Methods
حال، اگر بخواهیم متد ()think را در کلاس فرزند نادیده بگیریم، چه؟
باید مثال زیر را در نظر بگیریم که در آن دو کلاس ایجاد می کنیم و یکی از عملکردهای آن را در کلاس فرزند Overrid می کنیم.
open class ABC {
open fun think () {
println("Hey!! i am thinking ")
}
}
class BCD: ABC() { // inheritance happens using default constructor
override fun think() {
println("I am from Child")
}
}
fun main(args: Array) {
var a = BCD()
a.think()
}
خروجی :
I am from Child
عضوی که با کلمه کلیدی override علامت گذاری شده است، خود باز است، بنابراین ممکن است در زیر کلاس ها override شود.
اگر می خواهید override مجدد آن را ببندید ، باید آن را به صورت زیر نهایی کنید:
class BCD: ABC() {
final override fun think() {
println("I am from Child")
}
}
Overriding Properties
مکانیسم overriding بر روی properties به همان روشی کار می کند که روی متدها انجام می دهد.
properties در یک سوپرکلاس که سپس در یک کلاس مشتق شده مجدداً اعلام می شوند باید با کلمه کلیدی override مقدمه شوند و باید یک نوع compatible داشته باشند.
open class ABC {
open val count: Int = 0
open fun think () {
println("Hey!! i am thinking ")
}
}
class BCD: ABC() {
override val count: Int
init{
count = 100
}
override fun think() {
println("I am from Child")
}
fun displayCount(){
println("Count value is $count")
}
}
fun main(args: Array) {
var a = BCD()
a.displayCount()
}
خروجی :
Count value is 100
شما همچنین می توانید یک ویژگی val را با یک ویژگی var اورراید کنید، اما برعکس نه.
این قابل انجام است زیرا یک val property اساساً یک متد get را اعلام می کند و با overrid آن به عنوان var علاوه بر این، یک متد set در کلاس مشتق شده اعلام می شود.
همچنین می توانیم از کلمه کلیدی override به عنوان بخشی از اعلان property در Primary Constructor استفاده کنیم.
مثال زیر از Primary Constructor برای نادیده گرفتن خاصیت count استفاده می کند، که در صورتی که هیچ مقداری را به سازنده ارسال نکنیم، مقدار پیش فرض آن 400 خواهد بود:
open class ABC {
open val count: Int = 0
open fun think () {
println("Hey!! i am thinking ")
}
}
class BCD(override val count: Int = 400): ABC() {
override fun think() {
println("I am from Child")
}
fun displayCount(){
println("Count value is $count")
}
}
fun main(args: Array) {
var a = BCD(200)
var b = BCD()
a.displayCount()
b.displayCount()
}
خروجی :
Count value is 200
Count value is 400
Derived Class Initialization Order
وقتی یک شی از یک کلاس مشتق شده ایجاد می کنیم، مقداردهی اولیه سازنده از کلاس پایه شروع می شود.
این بدان معناست که اول از همه properties کلاس پایه مقداردهی اولیه می شود، پس از آن هر derived کلاس مشتق شده فراخوانی می شود و همین امر برای کلاس های مشتق شده دیگر نیز صدق می کند.
open class Base {
init{
println("I am in Base class")
}
}
open class Child: Base() {
init{
println("I am in Child class")
}
}
class GrandChild: Child() {
init{
println("I am in Grand Child class")
}
}
fun main(args: Array) {
var a = GrandChild()
}
خروجی :
I am in Base class
I am in Child class
I am in Grand Child class
Access Super Class Members
کد در یک کلاس مشتق شده می تواند توابع و properties سوپرکلاس خود را مستقیماً با استفاده از کلمه کلیدی super فراخوانی کند:
open class Base() {
open val name:String
init{
name = "Base"
}
open fun displayName(){
println("I am in " + this.name)
}
}
class Child(): Base() {
override fun displayName(){
super.displayName()
println("I am in " + super.name)
}
}
fun main(args: Array) {
var a = Child()
a.displayName()
}
کلاس های انتزاعی (Abstract Classes)
یک کلاس انتزاعی Kotlin مشابه کلاس انتزاعی جاوا است که قابل نمونه سازی نیست.
این بدان معناست که ما نمی توانیم اشیاء یک کلاس انتزاعی را ایجاد کنیم.
با این حال، ما می توانیم زیر کلاس ها را از یک کلاس انتزاعی Kotlin به ارث ببریم.
یک کلاس انتزاعی Kotlin با استفاده از کلمه کلیدی abstract در جلوی نام کلاس اعلام می شود.
properties و متدهای یک کلاس انتزاعی non-abstract (غیرانتزاعی) هستند مگر اینکه به صراحت از کلمه کلیدی abstract برای انتزاع کردن آنها استفاده کنیم.
اگر بخواهیم این اعضا را در کلاس فرزند override کنیم، فقط باید از کلمه کلیدی override در مقابل آنها در کلاس فرزند استفاده کنیم.
abstract class Person {
var age: Int = 40
abstract fun setAge() // Abstract Method
fun getAge() { // Non-Abstract Method
return age
}
}
کلاس های انتزاعی همیشه باز هستند.
برای ارث بردن زیر کلاس ها از آنها نیازی به استفاده صریح از کلمه کلیدی open ندارید.
در زیر یک مثال ساده وجود دارد که یک کلاس Kotlin Abstract و اجرای آن را از طریق یک کلاس فرزند نشان می دهد:
abstract class Person(_name: String) {
var name: String
abstract var age: Int
// Initializer Block
init {
this.name = _name
}
abstract fun setPersonAge(_age:Int)
abstract fun getPersonAge():Int
fun getPersonName(){
println("Name = $name")
}
}
class Employee(_name: String): Person(_name) {
override var age: Int = 0
override fun setPersonAge(_age: Int) {
age = _age
}
override fun getPersonAge():Int {
return age
}
}
fun main(args: Array) {
val employee = Employee("Zara")
var age : Int
employee.setPersonAge(20)
age = employee.getPersonAge()
employee.getPersonName()
println("Age = $age")
}
خروجی :
Name = Zara
Age = 20
در اینجا، یک کلاس Employee از یک کلاس انتزاعی Person مشتق شده است.
ما یک abstract property و دو متد انتزاعی را در کلاس فرزند Employee پیاده سازی کرده ایم.
نکته قابل توجه در اینجا این است که تمام اعضای انتزاعی در کلاس فرزند با کمک override حذف شده اند که در صورت به ارث بردن کلاس انتزاعی برای کلاس فرزند اجباری است.
خلاصه ی موارد مربوط به کلاس انتزاعی به شرح زیر می باشد :
به طور خلاصه، یک کلاس Kotlin که حاوی کلمه کلیدی abstract در اعلان خود است به عنوان کلاس انتزاعی شناخته می شود.
کلاس های انتزاعی ممکن است حاوی متدهای انتزاعی باشند یا نباشند.
اما، اگر یک کلاس حداقل یک متد انتزاعی داشته باشد، کلاس باید انتزاعی اعلام شود.
اگر کلاسی انتزاعی اعلام شود، نمی توان آن را نمونه سازی کرد.
برای استفاده از یک کلاس انتزاعی، باید آن را از کلاس دیگری به ارث برده، پیاده سازی هایی را برای متدهای انتزاعی موجود در آن ارائه کنید.
اگر یک کلاس انتزاعی را به ارث می برید، باید برای تمام متدهای انتزاعی موجود در آن پیاده سازی ارائه کنید.
Interface
در این بحش با Interface کاتلین آشنا خواهیم شد.
در Kotlin، این Interface دقیقاً مشابه جاوا 8 کار می کند، به این معنی که می توانند شامل پیاده سازی متد و همچنین اعلان متدهای abstract باشند.
یک Interface می تواند توسط یک کلاس به منظور استفاده از عملکرد تعریف شده آن پیاده سازی شود.
کلیدی “interface” برای تعریف یک Interface در Kotlin همانطور که در کد زیر نشان داده شده است استفاده می شود.
interface ExampleInterface {
var myVar: String // abstract property
fun absMethod() // abstract method
fun sayHello() = "Hello there" // method with default implementation
}
در مثال بالا، ما یک Interface به نام “ExampleInterface” ایجاد کردهایم و در داخل آن، چند abstract properties و متد را با هم داریم.
به تابعی با نام () SayHello نگاه کنید که یک متد implemented است.
در مثال زیر، Interface فوق را در یک کلاس پیاده سازی خواهیم کرد.
interface ExampleInterface {
var myVar: Int // abstract property
fun absMethod():String // abstract method
fun hello() {
println("Hello there, Welcome to manataz.Com!")
}
}
class InterfaceImp : ExampleInterface {
override var myVar: Int = 25
override fun absMethod() = "Happy Learning "
}
fun main(args: Array) {
val obj = InterfaceImp()
println("My Variable Value is = ${obj.myVar}")
print("Calling hello(): ")
obj.hello()
print("Message from the Website-- ")
println(obj.absMethod())
}
خروجی :
My Variable Value is = 25
! Calling hello (): Hello there, Welcome to manataz.Com
Message from the Website– Happy Learning
همانطور که قبلا ذکر شد، کاتلین از چندین وراثت پشتیبانی نمی کند، با این حال، همان چیز را می توان با پیاده سازی بیش از دو Interface در یک زمان به دست آورد.
در مثال زیر دو اینترفیس ایجاد می کنیم و بعداً هر دو اینترفیس را در یک کلاس پیاده سازی می کنیم.
interface A {
fun printMe() {
println(" method of interface A")
}
}
interface B {
fun printMeToo() {
println("I am another Method from interface B")
}
}
// implements two interfaces A and B
class multipleInterfaceExample: A, B
fun main(args: Array) {
val obj = multipleInterfaceExample()
obj.printMe()
obj.printMeToo()
}
در مثال بالا، دو Interface نمونه A، B ایجاد کردهایم و در کلاسی به نام “multipleInterfaceExample” دو اینترفیس که قبلا اعلام شده بود را پیادهسازی کردهایم.
خروجی :
method of interface A
I am another Method from interface B
Modifiers در کاتلین
Kotlin visibility modifiers کلمات کلیدی هستند که قابلیت مشاهده کلاسها، اشیاء، interface ، سازندهها، توابع و همچنین properties و Setters آنها را تعیین میکنند.
اگرچه دریافتکنندهها همیشه visibility یکسانی با properties دارند، بنابراین نمیتوانیم visibility آنها را تنظیم کنیم.
Setters توابعی هستند که برای تنظیم مقادیر properties استفاده می شوند، در حالی که به عنوان گیرنده ، توابعی هستند که برای به دست آوردن مقادیر آن properties استفاده می شوند.
چهار visibility modifiers در Kotlin وجود دارد که عبارتند از :
- public
- private
- protected
- internal
حالت پیش فرض public است.
این modifiers را میتوان در مکانهای متعددی مانند هدر کلاس یا بادی متدها استفاده کرد.
بیایید به جزئیات این modifiers نگاه کنیم :
Public Modifier
از هر نقطه در فضای کاری پروژه قابل دسترسی است.
در تمام مثالهای قبلی ما Modifier ذکر نکردهایم، از این رو، همه آنها در محدوده عمومی هستند.
در زیر مثالی برای درک بیشتر نحوه اعلان یک متغیر یا متد عمومی آورده شده است.
class publicExample {
val i = 1
fun doSomething() {
}
}
در مثال بالا، ما هیچ modifier را ذکر نکرده ایم، بنابراین متد و متغیری که در اینجا تعریف شده است، به طور پیش فرض عمومی هستند.
اگر چه مثال بالا را می توان با Public Modifier به طور صریح به صورت زیر نوشت :
public class publicExample {
public val i = 1
public fun doSomething() {
}
}
Private Modifier
کلاس ها، متدها، packages و سایر properties را می توان با یک Private Modifier اعلام کرد.
این Modifier دقیقاً معنای مخالف Public را دارد که به این معنی است که یک عضو خصوصی خارج از محدوده آن قابل دسترسی نیست.
هنگامی که هر چیزی به عنوان Private اعلام شد، میتوان آن را فقط در محدوده بلافصل خود در دسترس قرار داد.
به عنوان مثال، یک پکیج خصوصی می تواند در آن فایل خاص قابل دسترسی باشد.
یک کلاس یا رابط خصوصی فقط توسط اعضای داده آن قابل دسترسی است.
private class privateExample {
private val i = 1
private val doSomething() {
}
در مثال بالا، کلاس privateExample فقط از داخل همان فایل منبع قابل دسترسی است و متغیر i و متد doSomething فقط از داخل کلاس privateExample قابل دسترسی است.
مثال :
open class A() {
private val i = 1
fun doSomething(){
println("Inside doSomething" )
println("Value of i is $i" )
}
}
class B : A() {
fun printValue(){
doSomething()
// println("Value of i is $i" )
}
}
fun main(args: Array) {
val a = A()
val b = B()
b.printValue()
}
خروجی :
Inside doSomething
Value of i is 1
در اینجا ما نمیتوانیم به متغیر i در کلاس B دسترسی پیدا کنیم، زیرا به عنوان Private تعریف شده است، به این معنی که میتوان در داخل کلاس به آن دسترسی داشت و نه جای دیگری.
Protected Modifier
Protected یکی دیگر از Modifier های دسترسی برای Kotlin است که در حال حاضر برای اعلام سطح بالا در دسترس نیست، زیرا هیچ پکجی قابل محافظت نیست.
یک کلاس یا رابط یا پراپرتی یا تابع محافظت شده برای خود کلاس و فقط زیر کلاسهای آن قابل مشاهده است.
package one;
class A() {
protected val i = 1
}
class B : A() {
fun getValue() : Int {
return i
}
}
در مثال بالا، متغیر i به عنوان Protected اعلام شده است، بنابراین، فقط برای خود کلاس و زیر کلاس های آن قابل مشاهده است.
مثال :
open class A() {
protected val i = 1
protected fun doSomething(){
println("Inside doSomething" )
println("Value of i is $i" )
}
}
class B : A() {
fun printValue(){
doSomething()
println("Value of i is $i" )
}
}
fun main(args: Array) {
val a = A()
val b = B()
//a.doSomething()
b.printValue()
}
خروجی :
Inside doSomething
Value of i is 1
Value of i is 1
در اینجا ما نمیتوانیم () doSomething را حتی با استفاده از یک آبجکت از کلاس A فراخوانی کنیم، زیرا به عنوان Protected تعریف شده است، به این معنی که فقط در خود کلاس یا در زیر کلاسهای آن قابل دسترسی است.
Internal Modifier
Internal یک modifier جدید در Kotlin است.
اگر هر چیزی به عنوان Internal علامت گذاری شود، فیلد خاص به عنوان فیلد Internal علامت گذاری می شود.
یک پکیج Internal فقط در داخل ماژولی که تحت آن پیاده سازی شده است قابل مشاهده است.
یک رابط کلاس Internal فقط توسط کلاس های دیگر موجود در همان بسته یا ماژول قابل مشاهده است.
در مثال زیر نحوه پیاده سازی یک متد Internal را خواهیم دید.
package one
internal class InternalExample {
}
class publicExample{
internal val i = 1
internal fun doSomething() {
}
}
در مثال بالا، کلاس InternalExample فقط از داخل همان ماژول قابل دسترسی است، به طور مشابه متغیر i و تابع () doSomething نیز فقط از داخل همان ماژول قابل دسترسی هستند، حتی اگر کلاس publicExample از هرجایی قابل دسترسی باشد، زیرا این کلاس به صورت پیشفرض دارای دید Public است.
مثال :
package com.manataz.modifiers
open class A() {
internal val i = 1
internal fun doSomething(){
println("Inside doSomething" )
println("Value of i is $i" )
}
}
class B : A() {
fun printValue(){
doSomething()
println("Value of i is $i" )
}
}
fun main(args: Array) {
val a = A()
val b = B()
a.doSomething()
b.printValue()
}
خروجی :
Inside doSomething
Value of i is 1
Inside doSomething
Value of i is 1
Value of i is 1
Extensions
اکستنشن های Kotlin توانایی گسترش یک کلاس با عملکردهای جدید را بدون پیاده سازی مفهوم وراثت توسط یک کلاس یا با استفاده از الگوی طراحی مانند Decorator فراهم می کنند.
این افزونه ها اساساً برخی از عملکردها را در یک کلاس موجود بدون گسترش کلاس اضافه می کنند.
Extensions اجازه می دهد تا توابع جدیدی را برای یک کلاس از یک کتابخانه شخص ثالث بدون تغییر کلاس بنویسید.
زیبایی توابع Extensions در این است که می توان آنها را به روش معمول فراخوانی کرد، انگار که متدهای کلاس اصلی هستند و این توابع جدید را Extension Function می نامند.
به طور مشابه، ما همچنین میتوانیم extension properties را برای یک کلاس Kotlin موجود تعریف کنیم.
Extension Function
تابع Extension یک تابع عضو از یک کلاس است که خارج از کلاس تعریف شده است.
توابع Extension ایجاد شده به عنوان یک تابع منظم در آن کلاس استفاده می شود.
در زیر سینتکسی برای تعریف یک تابع Extension آمده است.
در اینجا تابع Extension با استفاده از نام کلاس و همچنین با استفاده از نام متد اعلان می شود.
fun .(){
....
function body
}
در تابع Extension ، Kotlin اجازه می دهد تا یک متد را خارج از کلاس اصلی تعریف کنید.
در مثال زیر خواهیم دید که چگونه Extension در سطح تابع پیاده سازی می شود.
class Alien {
var skills : String = "null"
fun printMySkills() {
print(skills)
}
}
fun main(args: Array) {
var a1 = Alien()
a1.skills = "JAVA"
//a1.printMySkills()
var a2 = Alien()
a2.skills = "SQL"
//a2.printMySkills()
var a3 = Alien()
a3.skills = a1.addMySkills(a2)
a3.printMySkills()
}
fun Alien.addMySkills(a:Alien):String{
var a4 = Alien()
a4.skills = this.skills + " " +a.skills
return a4.skills
}
در مثال بالا، ما هیچ متدی در داخل کلاس Alien به نام () addMySkills نداریم، با این حال، ما هنوز همان متد را در جایی خارج از کلاس پیادهسازی میکنیم، این همان جادوی Extension است.
هنگامی که برنامه Kotlin فوق را اجرا می کنید، خروجی زیر را ایجاد می کند :
JAVA SQL
کتابخانه ها
Kotlin اجازه می دهد تا کلاس های استاندارد کتابخانه و همچنین کلاس های تعریف شده توسط کاربر را گسترش دهید.
به عنوان مثال، اگر به یک تابع تخصصی برای کلاس استاندارد Kotlin String نیاز دارید که تعداد حروف صدادار موجود در رشته را برمی گرداند، چنین روشی از قبل در کلاس String موجود نیست اما می توانید از یک تابع Extension برای انجام این کار استفاده کنید.
fun main(args: Array) {
val str = "Good morning Kotlin"
val result = str.countVowels()
println("Number of vowels: $result")
}
fun String.countVowels(): Int{
var vowels = 0
for (i in 0.. this.length - 1) {
val ch = this[i]
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') {
++vowels
}
}
return vowels;
}
خروجی :
Number of vowels: 6
Companion Object Extensions
کاتلین مکانیسم دیگری را برای پیاده سازی عملکرد ایستا جاوا فراهم می کند.
این را می توان با استفاده از Companion Object که در داخل یک کلاس اعلام شده و با کلمه کلیدی Companion علامت گذاری شده است به دست آورد.
با استفاده از این مکانیسم، میتوانیم یک آبجکت از یک کلاس را در یک متد factory ایجاد کنیم و بعداً میتوانیم آن متد را با استفاده از رفرنس نام کلاس فراخوانی کنیم.
در مثال زیر، یک ” Companion Object ” ایجاد خواهیم کرد.
fun main(args: Array) {
println("Heyyy!!!"+A.show())
}
class A {
companion object {
fun show():String {
return("You are learning Kotlin from manataz.com")
}
}
}
خروجی :
Heyyy!!! You are learning Kotlin from manataz.com
مثال بالا در جاوا ثابت به نظر می رسد، با این حال، به صورت ریل تایم، ما در حال ایجاد یک آبجکت به عنوان متغیر عضو همان کلاس هستیم.
به همین دلیل است که تحت extension property نیز قرار می گیرد و می توان آن را به عنوان یک object extension نامید.
شما اساساً آبجکت همان کلاس را برای استفاده از برخی از توابع عضو گسترش می دهید.
Extension Properties
Kotlin اجازه می دهد تا Extension Properties را به متدی بسیار مشابه مانند extension function تعریف کنیم.
Extension properties نیز خارج از کلاس تعریف شده است.
از آنجایی که extensions در واقع اعضا را در کلاسها وارد نمیکنند، هیچ راه موثری برای داشتن یک فیلد backing به extension property وجود ندارد.
به همین دلیل است که initializers برای extension properties مجاز نیستند.
می توانیم getter و setter را به همراه property هایی که چیزی جز extension functions نیستند اضافه کنیم.
class Temperature(var celsius: Float)
fun main(args: Array) {
val t = Temperature(40f)
println(t.fahrenheit)
t.fahrenheit = 85f
println(t.celsius)
}
var Temperature.fahrenheit: Float
get() = (celsius * 9 / 5) + 32
set(value) {
celsius = (value - 32) * 5 / 9
}
خروجی :
104.0
29.444445
Data Classes
در این بخش با کلاس های Data در آموزش کلاس ها و آبجکت های کاتلین بیشتر آشنا می شویم.
البته ما در بخش های قبلی به کرار از آن ها نام بردیم ، اما در این قسمت مفهوم آنها را بررسی خواهیم کرد.
یک کلاس هر زمان که به عنوان “ Data” علامت گذاری شود می تواند به عنوان یک کلاس داده معرفی شود.
از این نوع کلاس می توان برای جدا نگه داشتن داده های اصلی استفاده کرد.
به غیر از این، هیچ عملکرد دیگری ارائه نمی دهد.
تمام کلاس های داده باید یک primary constructor داشته باشند و تمام primary constructor باید حداقل یک پارامتر داشته باشند.
هر زمان که یک کلاس به عنوان Data علامت گذاری می شود، می توانیم از برخی از تابع های داخلی آن کلاس داده مانند () toString () ، hashCode و … استفاده کنیم.
هر کلاس داده ای نمی تواند modifier مانند abstract و open یا internal داشته باشد.
کلاس داده را می توان به کلاس های دیگر نیز گسترش داد.
در مثال زیر یک کلاس داده ایجاد می کنیم.
fun main(args: Array) {
val book: Book = Book("Kotlin", "manataz.com", 5)
println("Name of the Book is--"+book.name) // "Kotlin"
println("Puclisher Name--"+book.publisher) // "manataz.com"
println("Review of the book is--"+book.reviewScore) // 5
book.reviewScore = 7
println("Printing all the info all together--"+book.toString())
//using inbuilt function of the data class
println("Example of the hashCode function--"+book.hashCode())
}
data class Book(val name: String, val publisher: String, var reviewScore: Int)
قطعه کد بالا خروجی زیر را در مرورگر ایجاد می کند، جایی که ما یک کلاس داده برای نگهداری برخی از داده ها ایجاد کرده ایم و از تابع اصلی به همه اعضای داده آن دسترسی پیدا کرده ایم.
Name of the Book is--"Kotlin"
Puclisher Name--"manataz.com"
Review of the book is--5
Printing all the info all together--(name-Kotlin, publisher-manataz.com, reviewScore-7)
Example of the hashCode function---1753517245
Generics
مانند جاوا، Kotlin مرتبه بالاتری از تایپ متغیر به نام Generics را ارائه می دهد.
در این بخش ، نحوه پیادهسازی Generics و اینکه چگونه به عنوان یک توسعهدهنده میتوانیم از قابلیتهای ارائه شده در کتابخانه ژنریک استفاده کنیم، خواهیم آموخت.
از نظر پیادهسازی، ژنریکها تقریباً شبیه جاوا هستند، اما توسعه دهنده Kotlin دو کلمه کلیدی جدید out و in را معرفی کرده است تا کدهای Kotlin را برای توسعه دهنده خواناتر و آسانتر کند.
طبق مثال، List یک کلاس در Kotlin است، در حالی که <String> List یک نوع در Kotlin است.
مثال زیر نحوه پیاده سازی ژنریک در کاتلین را نشان می دهد.
fun main(args: Array) {
val integer: Int = 1
val number: Number = integer
print(number)
}
در کد بالا یک عدد صحیح اعلام کردیم و بعداً آن متغیر را به یک متغیر عددی اختصاص دادیم.
این کار امکان پذیر است زیرا Int یک زیر کلاس از کلاس Number است، بنابراین تبدیل به طور خودکار و ریل تایم اتفاق می افتد و خروجی را به صورت “1” تولید می کند.
اجازه دهید چیزهای بیشتری در مورد ژنریک در Kotlin ارائه دهیم.
بهتر است هر زمان که از نوع داده ای که قرار است در برنامه استفاده کنیم مطمئن نیستیم به سراغ نوع داده generic برویم.
به طور کلی، ژنریک با <T> تعریف می شود که در آن T مخفف template است، که می تواند به صورت پویا توسط کاتلین تعیین شود.
در مثال زیر نحوه استفاده از انواع داده های generic در زبان برنامه نویسی Kotlin را خواهیم دید.
fun main(args: Array) {
var objet = genericsExample("JAVA")
var objet1 = genericsExample(10)
}
class genericsExample(input:T) {
init {
println("I am getting called with the value "+input)
}
}
در کد بالا، یک کلاس با نوع بازگشتی ایجاد می کنیم که به صورت <T> نمایش داده می شود.
به متد اصلی نگاهی بیندازید، جایی که ما به صورت پویا مقدار آن را در اجرا با اثبات نوع مقدار تعریف کردهایم، در حالی که آبجکت این کلاس را ایجاد میکنیم.
این است که چگونه ژنریک توسط کامپایلر Kotlin تفسیر می شود.
زمانی که این کد را در زمینه کدگذاری خود اجرا کنیم، خروجی زیر را در مرورگر دریافت خواهیم کرد.
I am getting called with the value JAVA
I am getting called with the value 10
وقتی میخواهیم نوع generic را به هر یک از انواع فوقالعاده آن اختصاص دهیم، باید از کلمه کلیدی out استفاده کنیم و زمانی که میخواهیم نوع generic را به هر یک از انواع فرعی آن اختصاص دهیم، باید از in استفاده کنیم.
fun main(args: Array) {
var objet1 = genericsExample(10)
var object2 = genericsExample(10.00)
println(objet1)
println(object2)
}
class genericsExample(input:T) {
init {
println("I am getting called with the value "+input)
}
}
خروجی :
I am getting called with the value 10
I am getting called with the value 10.0
genericsExample@28d93b30
genericsExample@1b6d3586
Delegation
کاتلین با معرفی یک کلمه کلیدی جدید تحت عنوان “by” از الگوی طراحی “Delegation” پشتیبانی می کند.
با استفاده از این کلمه کلیدی یا متدولوژی delegation ، کاتلین به کلاس مشتق شده اجازه می دهد تا از طریق یک آبجکت خاص به تمام متدهای public پیاده سازی شده توسط یک interface دسترسی پیدا کند.
مثال زیر نشان می دهد که چگونه این اتفاق در کاتلین رخ می دهد.
interface Base {
fun printMe() //abstract method
}
class BaseImpl(val x: Int) : Base {
override fun printMe() { println(x) } //implementation of the method
}
class Derived(b: Base) : Base by b // delegating the public method on the object b
fun main(args: Array) {
val b = BaseImpl(10)
Derived(b).printMe() // prints 10 :: accessing the printMe() method
}
در مثال، یک اینترفیس “Base” با متد abstract آن به نام “() printme” داریم.
در کلاس BaseImpl، ما این () printme را پیادهسازی میکنیم و بعداً از کلاس دیگری با استفاده از کلمه کلیدی by از این پیادهسازی استفاده میکنیم.
کد بالا خروجی زیر را در مرورگر خواهد داشت.
10
استفاده از () Lazy
Lazy یک تابع lambda است که یک property را به عنوان ورودی می گیرد و در ازای آن یک نمونه از <Lazy<T می دهد، که در آن <T> اساساً نوع property هایی است که استفاده می کند.
اجازه دهید نگاهی به مثال زیر بیندازیم تا بفهمیم چگونه کار می کند.
val myVar: String by lazy {
"Hello"
}
fun main(args: Array) {
println(myVar +" My dear friend")
}
در کد بالا، یک متغیر myVar را به تابع Lazy میدهیم که در عوض مقداری را به آبجکت آن اختصاص میدهد و همان را به تابع اصلی برمیگرداند.
در زیر خروجی در مرورگر آمده است.
Hello My dear friend
() Delegetion.Observable
() Observable دو آرگومان برای مقداردهی اولیه آبجکت می گیرد و همان آرگومان را به تابع فراخوانده شده برمی گرداند.
در مثال زیر، نحوه استفاده از متد () Observable را برای پیاده سازی Delegetion اختیار خواهیم دید.
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("Welcome to manataz.com") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array) {
val user = User()
user.name = "first"
user.name = "second"
}
خروجی :
first -> second
به طور کلی، سینتکس عبارتی است که پس از واگذاری کلمه کلیدی by متدهای () get و () set متغیر p به متدهای () getValue و () setValue تعریف شده در کلاس Delegate ، delegated می شوند.
class Example {
var p: String by Delegate()
}
برای قطعه کد بالا، کلاس delegate که برای تخصیص مقدار در متغیر p باید ایجاد کنیم، در زیر آمده است.
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
در حین خواندن، متد ()getValue فراخوانی می شود و در هنگام تنظیم متغیر متد () setValue فراخوانی می شود.
Exception Handling
در آخرین بخش آموزش از قسمت سوم آموزش زبان کاتلین (آموزش آبجکت ها و کلاس ها در کاتلین) قصد داریم Exception handling را معرفی کنیم.
Exception handling بخش بسیار مهمی از یک زبان برنامه نویسی است.
این تکنیک برنامه ما را از تولید خروجی اشتباه در زمان اجرا محدود می کند.
در این بخش، نحوه رسیدگی به Exception Handling را به صورت ریل تایم در Kotlin خواهیم گرفت.
Exception در Kotlin بسیار شبیه به Exception در جاوا هستند.
همه Exception از نوادگان طبقه ” Throwable ” هستند.
مثال زیر نحوه استفاده از تکنیک Exception Handling را در کاتلین را نشان می دهد.
fun main(args: Array) {
try {
val myVar:Int = 12;
val v:String = "manataz.com";
v.toInt();
} catch(e:Exception) {
e.printStackTrace();
} finally {
println("Exception Handeling in Kotlin");
}
}
در کد بالا، ما یک String را اعلام کردیم و بعداً آن رشته را به عدد صحیح گره زدیم، که در واقع یک Exception در حالت ریل تایم است.
از این رو، خروجی زیر را در مرورگر دریافت خواهیم کرد.
;val myVar:Int = 12
Exception Handeling in Kotlin
مانند جاوا، کاتلین نیز بلوک finally را پس از اجرای بلوک catch اجرا می کند.
مهرسا امینی
برنامه نویس ، انیماتور ، سئوکار
در زندگی رویاهات را دنبال کن