Kotlin

Bridge to Kotlin Part 4 (Classes and Objects)

အရင်အပိုင်းတွေ လေ့လာချင်တယ်ဆိုရင်

Classes and Objects

Kotlin မှာ classes တွေ object တွေ တည်ဆောက်တဲ့အခါ ကျွန်တော်တို့ Java မှာ သုံးနေသလို မဟုတ်ပဲ မူကွဲလေးတွေ ရှိလာပါတယ်။ ဘာတွေ ကွဲပြားသွားလဲ လေ့လာကြည့်ရအောင်။ ပထမဦးဆုံး ကွဲပြားသွားတာက constructor တည်ဆောက်မှု ပုံစံပါ။ ကျွန်တော်တို့ Android မှာ class တွေကို တည်ဆောက်တဲ့အခါ Primary Constructor တစ်ခုတည်းကို သုံးရတာ များပါတယ်။ Java ကို သုံးပြီး ရေးနေတဲ့အတွက် Primary Constructor အပြင် တစ်ခြား မိမိ စိတ်ကြိုက် Constructor တွေ အများကြီးလဲ သုံးလို့ရနေပါတယ်။

ကျွန်တော့်အထင် Kotlin အနေနဲ့ Primary Constructor တစ်ခုတည်း သုံးစရာ လိုလေ့ရှိတဲ့ Android Developer တွေအတွက်လဲ အဆင်ပြေစေ၊ Java အတွက်လဲ Interoperability လဲ ရစေဆိုပြီး စဉ်းစားပုံ ရပါတယ်။ ကျွန်တော်တို့ နမူနာ ရေးကြည့်ရအောင်

fun main(args: Array<String>) {
    val primary = Primary("Doe")
    val primaryCon = PrimaryConstructor("John")
    primary.testCall()
    primaryCon.testCall()
}

class Primary constructor(val firstName: String) {
    fun testCall() {
        println("First Name : $firstName")
    }
} 

class PrimaryConstructor(val firstName: String) {
    fun testCall() {
        println("First Name : $firstName")
    }
}

Primary Constructor တစ်ခုတည်း သုံးချင်တယ်ဆိုရင် အပေါ်မှာ ပြထားတဲ့အတိုင်း ရေးရုံပါပဲ။ val သုံးမလား၊ var သုံးမလားဆိုတာကတော့ ကိုယ်အသုံးပြုချင်တဲ့ data အပေါ် မူတည်ပါတယ်။ ကျွန်တော်တို့ Java မှာလို  Secondary တွေ သုံးချင်တယ်ဆိုရင်တော့ အောက်မှာ ပြထားတဲ့အတိုင်း ရေးရမှာ ဖြစ်ပါတယ်။

fun main(args: Array<String>) {
    val myTest = MyTest("Maung")
    val anotherTest = MyTest("Maung", "Maung")
    myTest.testCall()
    anotherTest.testAnotherCall()
}

class MyTest(val firstName: String) {
    var lastName: String? = null
    constructor(firstName: String, lastName: String): this(firstName) {
        this.lastName = lastName
    }
    fun testCall() {
        println("First Name : $firstName")
    }
    fun testAnotherCall() {
        println("Name : $firstName $lastName")
    }
}

Primary Constructor တွေမှာ ဆိုရင်တော့ val ဖြစ်ဖြစ် var ဖြစ်ဖြစ် တန်းသုံးလို့ရပါတယ်။ second variable assignment လုပ်စရာမလိုပါဘူး။ Secondary Constructor တွေမှာတော့ second variable assignment ထည့်ပေးမှ ရပါမယ်။ နောက်တစ်ခုက Secondary Constructor တွေမှာ this call ထည့်ပေးဖို့ လိုပါတယ်။

ကျွန်တော်တို့ ဒီနေရာမှာ လိုအပ်ရင် ထပ်ပြီး လေ့လာဖို့ လိုတာလေးတွေ ရှိပါသေးတယ်။ အဲဒါတွေကို Kotlin Reference Guide မှာ ဆက်ပြီး လေ့လာဖို့ လိုပါတယ်။

Classes, Interface and Inheritance

Class၊ Interface နဲ့ Inheritance တွေကတော့ ရေးပုံကွဲသွားတာကလွဲလို့ ကျွန်တော်တို့ ရေးနေကြပုံစံတွေနဲ့ အတူတူပါပဲ။ ထူးခြားတာလေးတွေကို ရွေးပြီး ပြောသွားပါမယ်။

Inheritance နဲ့ ပတ်သက်ပြီး ပထမဦးဆုံး ထူးခြားတဲ့ အချက်တစ်ခုကတော့ ဘယ် Class ကိုမဆို ကြော်ငြာလိုက်ပြီဆိုတာနဲ့ Any ဆိုတဲ့ class ကို inherit လုပ်တယ်ဆိုတာပါ။ အဓိက ရည်ရွယ်ချက်ကတော့ equals(), hashCode() နဲ့ toString() စတာတွေအတွက် ဖြစ်ပါတယ်။

Class တွေကို Inheritance လုပ်တဲ့အခါ ရေးသားတဲ့ ပုံစံအနည်းငယ် ကွဲပြားခြားနားမှု ရှိပါတယ်။ ကျွန်တော်တို့ လက်တွေ့ရေးကြည့်ရအောင်

fun main(args: Array<String>) {
    val second = Second("Aung Aung", "Hledan")
    println(second.name)
    println(second.address)
}
open class First(val name: String)
class Second(name: String, val address: String) : First(name)

အပေါ်က code ကို လေ့လာကြည့်မယ်ဆိုရင် First ဆိုတဲ့ class က open ဆိုပြီး သတ်မှတ်ပေးထားပါတယ်။ အဲဒါမှပဲ Second ဆိုတဲ့ class က extend လုပ်လို့ရပါမယ်။ super class variable passing ကလဲ လွယ်လွယ်လေးပါ။ ရှေ့မှာလဲထည့် နောက်မှာလဲ ကွင်းစကွင်းပိတ်နဲ့ ထည့်ပေးလိုက်တာနဲ့ ရပါပြီ။

နောက်တစ်ခု သတိထားဖို့ လိုတာက Kotlin မှာ ကြော်ငြာလိုက်တဲ့ class တိုင်း open ဆိုပြီး မသတ်မှတ်ပေးရင် အားလုံး final ပါပဲ။ အပြောင်းအလဲမရှိဆိုတဲ့ သဘောပါ။

Primary Constructor and initializer blocks

Primary Constructor မှာ ကျွန်တော်တို့ ရေးနေကြ ပုံစံတွေနဲ့ မတူတဲ့ အချက်လေးတွေ ရှိပါသေတယ်။ အဲဒါတွေကို ဆက်ပြီး လေ့လာကြရအောင်။ ကျွန်တော်တို့ primary constructor တွေကို သုံးလိုက်တယ်ဆိုရင် တစ်ခြား ဘာမှ ရေးလို့ မရတော့ပါဘူး။ ဥ့ပမာ – primary constructor မှာ တစ်ခြား method တစ်ခုကို ခေါ်ချင်တဲ့အခါ ခေါ်လို့မရတော့ပါဘူး။ အဲဒီလို ခေါ်မယ်ဆိုရင် ဘယ်လို ခေါ်မလဲ လေ့လာကြည့်ရအောင်

fun main(args: Array<String>) {
    val primaryClass = PrimaryClass("John")
}

class PrimaryClass(val firstName: String) {
    val secondVal: String = "Hello from Kotlin"
    init {
        first()
        println("Second Val $secondVal")
    }

    val thirdVal: String = "Hello from Kotlin Again"
    init {
        second()
        println("Third Val $thirdVal")
    }

    fun first() {
        println("Hello from First")
    }

    fun second() {
        println("Hello from Second")
    }
}

primary constructor အတွက် လုပ်စရာရှိတာ လုပ်ချင်တယ်ဆိုရင် အပေါ်မှာ ပြထားတဲ့ program အတိုင်း init() ဆိုတဲ့ block တွေနဲ့ ရေးလို့ရပါတယ်။ ကျွန်တော်ရေးထားတဲ့ ကုဒ်မှာ init() ကို တစ်ခုတည်း မဟုတ်ပဲ နှစ်ခု ရေးထားတာ သတိထားမိပါလိမ့်မယ်။ ကြိုက်သလောက် init() လုပ်လို့ရပါတယ်။ သူ့အစီအစဉ်အတိုင်း လုပ်ဆောင်သွားပါလိမ့်မယ်။

Primary constructor အတွက် multiple initializer block သုံးလို့ရပါတယ်။ ဒါကလဲ အရင်လို constructor တစ်ခု initializer block တစ်ခု သုံးနေတာမျိုး မဟုတ်တော့ပဲ ပိုပြီး လွတ်လပ်သွားစေပါတယ်။ ကျွန်တော်တို့ အပေါ်က Program လေးကို ပြင်ရေးကြည့်မယ်ဆိုရင် အဲဒီလို multiple initializer block တွေ အများကြီး ရေးလို့ရတဲ့အတွက် ရလာတဲ့ အကျိုးကျေးဇူးတွေထဲက တစ်ခုကို မြင်နိုင်ပါလိမ့်မယ်။

fun main(args: Array<String>) {
    val calculator = Calculator(5600)
}

class Calculator(val firstVal: Int) {
    var sum: Int = 0
    init {
        sum += firstVal
    }
    val secondVal = 400
    init {
        sum += secondVal
    }
    val thirdVal = 600
    init {
        sum += thirdVal
        println(sum)
    }
}

Initializer တွေကို Chain တစ်ခုလို အသုံးချလို့ ရလာစေပါတယ်။ Method Chain ပုံစံနဲ့ ဆင်တူပါတယ်။

Init Blocks and Secondary Constructors

Kotlin မှာ primary constructor က secondary constructor တွေတိုင်းကို delegate လုပ်ပါတယ်။ အဲဒီအတွက် primary constructor ရဲ့  init block တွေတိုင်းဟာလဲ secondary constructor တွေမှာ အလုပ်အရင်လုပ်ပါတယ်။ ကျွန်တော်တို့ ဥပမာ ရေးကြည့်ရအောင်

fun main(args: Array<String>) {
    val calculator = Calculator(5600)
    val anotherCal = Calculator(2300, 7800)
}

class Calculator(val firstVal: Int) {
    var sum: Int = 0
    init {
        sum += firstVal
    }
    val secondVal = 400
    init {
        sum += secondVal
    }
    val thirdVal = 600
    init {
        sum += thirdVal
        println(sum)
    }

    constructor(firstVal: Int, secondVal: Int): this(firstVal) {
        sum += secondVal
        println(sum)
    }
}

အဲဒီလို ရေးကြည့်လိုက်မယ်ဆိုရင် ကျွန်တော်တို့ init block တွေနဲ့ secondary constructor တွေရဲ့ ဆက်စပ်ပုံတွေကို သိလာပါလိမ့်မယ်။

ဒီနေရာမှာ တစ်ခု သတိထားဖို့ လိုတာက ကျွန်တော်တို့ Java မှာလို Constructor တွေဟာ သီးခြားလွတ်လပ်တဲ့ object တွေအဖြစ် ကွဲထွက်သွားတာမျိုးမဟုတ်ပဲ Secondary Constructor တွေက Primary Constructor တွေကို တာဝန်ခံတယ် ဆိုတာပါ။

ဆက်ပါဦးမယ်