เรียนภาษาโปรแกรม Kotlin

Kotlin เป็นภาษาโปรแกรม ใช้กันอย่างแพร่หลายในหมู่นักพัฒนาซอฟต์แวร์ Android หัวข้อนี้ทำหน้าที่เป็น Kotlin หลักสูตรเร่งรัดที่จะช่วยให้คุณพร้อมทำงานได้อย่างรวดเร็ว

การประกาศตัวแปร

Kotlin ใช้คีย์เวิร์ด 2 คำที่แตกต่างกันเพื่อประกาศตัวแปร: val และ var

  • ใช้ val สำหรับตัวแปรที่ไม่มีการเปลี่ยนแปลงค่า คุณจะกำหนดค่าใหม่ไม่ได้ ให้กับตัวแปรที่ประกาศโดยใช้ val
  • ใช้ var สําหรับตัวแปรที่ค่าเปลี่ยนแปลงได้

ในตัวอย่างด้านล่าง count เป็นตัวแปรประเภท Int ที่ได้รับการกำหนด ค่าเริ่มต้นของ 10:

var count: Int = 10

Int เป็นประเภทที่แสดงจำนวนเต็ม ซึ่งเป็นหนึ่งในประเภทตัวเลขจำนวนมาก แสดงใน Kotlin ได้ เช่นเดียวกับภาษาอื่นๆ คุณสามารถใช้ Byte, Short, Long, Float และ Double ขึ้นอยู่กับข้อมูลตัวเลขของคุณ

คีย์เวิร์ด var หมายความว่าคุณสามารถกำหนดค่าให้กับ count ใหม่ได้ตามต้องการ สำหรับ เช่น คุณเปลี่ยนค่าของ count จาก 10 เป็น 15 ได้

var count: Int = 10
count = 15

อย่างไรก็ตาม ค่าบางค่าไม่จำเป็นต้องเปลี่ยน ลองใช้ String ที่เรียกว่า languageName หากต้องการตรวจสอบว่า languageName มีค่าเสมอ ของ "Kotlin" คุณสามารถประกาศ languageName โดยใช้คีย์เวิร์ด val ดังนี้

val languageName: String = "Kotlin"

คีย์เวิร์ดเหล่านี้ช่วยให้คุณระบุสิ่งที่เปลี่ยนแปลงได้ ใช้ข้อมูลดังกล่าวเพื่อ ข้อได้เปรียบของคุณตามความจำเป็น หากต้องกำหนดการอ้างอิงตัวแปรใหม่ ประกาศเป็น var หรือไม่เช่นนั้น ให้ใช้ val

การอนุมานประเภท

ต่อจากตัวอย่างก่อนหน้านี้ เมื่อคุณกำหนดค่าเริ่มต้นให้กับ languageName คอมไพเลอร์ Kotlin จะอนุมานประเภทได้จากประเภทของ มูลค่าที่กำหนด

เนื่องจากค่าของ "Kotlin" เป็นประเภท String คอมไพเลอร์จึงอนุมานว่า languageName ยังเป็นStringด้วย โปรดทราบว่า Kotlin เป็นแอปที่พิมพ์แบบคงที่ ภาษา ซึ่งหมายความว่าประเภทจะได้รับการแก้ไขในเวลาคอมไพล์ และไม่ การเปลี่ยนแปลง

ในตัวอย่างต่อไปนี้ languageName จะอนุมานเป็น String คุณจึงไม่สามารถ เรียกใช้ฟังก์ชันที่ไม่ได้เป็นส่วนหนึ่งของคลาส String:

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

// Fails to compile
languageName.inc()

toUpperCase() คือฟังก์ชันที่เรียกใช้ได้เฉพาะตัวแปรประเภท String เนื่องจากคอมไพเลอร์ Kotlin อนุมาน languageName เป็น String คุณสามารถโทรหา toUpperCase() ได้อย่างปลอดภัย อย่างไรก็ตาม inc() เป็นโอเปอเรเตอร์ Int ฟังก์ชัน เพื่อให้ไม่สามารถเรียกใน String วิธีการพิมพ์ของ Kotlin การอนุมานทำให้คุณได้ทั้งความกระชับและความปลอดภัยในประเภท

ไม่มีข้อมูลความปลอดภัย

ในบางภาษา สามารถประกาศตัวแปรประเภทการอ้างอิงโดยไม่ต้องระบุ ค่าที่ชัดเจนเริ่มต้น ในกรณีเหล่านี้ ตัวแปรมักจะมีค่า Null ตัวแปร Kotlin เก็บค่า Null ตามค่าเริ่มต้นไม่ได้ ซึ่งหมายความว่า ข้อมูลโค้ดต่อไปนี้ไม่ถูกต้อง:

// Fails to compile
val languageName: String = null

ตัวแปรที่จะเก็บค่า Null ต้องเป็นตัวแปรประเภท nullable คุณสามารถ ระบุว่าตัวแปรเป็นค่าว่างได้โดยใส่ ? ต่อท้ายประเภท ในตัวอย่างต่อไปนี้

val languageName: String? = null

เมื่อใช้ประเภท String? คุณจะกำหนดค่า String หรือ null ให้กับได้ languageName

คุณต้องจัดการกับตัวแปรที่เป็นค่าว่างอย่างรอบคอบ ไม่เช่นนั้นจะเสี่ยงให้ NullPointerException เช่น ใน Java หากคุณพยายามจะเรียกเมธอด หากเป็นค่าว่าง โปรแกรมของคุณจะขัดข้อง

Kotlin มีกลไกมากมายสำหรับการทำงานกับ Null อย่างปลอดภัย ตัวแปร สำหรับข้อมูลเพิ่มเติม โปรดดู รูปแบบ Kotlin ที่พบบ่อยใน Android: ความสามารถในการเว้นว่าง

เงื่อนไข

Kotlin มีกลไกหลายอย่างในการนำตรรกะตามเงื่อนไขไปใช้ มากที่สุด โดยทั่วไปจะเป็นคำสั่ง if-else ถ้านิพจน์ที่อยู่ใน วงเล็บถัดจากคีย์เวิร์ด if ประเมินได้เป็น true จากนั้นเขียนโค้ดภายใน สาขานั้น (เช่น โค้ดที่ตามมาทันทีซึ่งห่อด้วยปีกกา วงเล็บปีกกา) มิเช่นนั้น ระบบจะเรียกใช้โค้ดภายใน Branch else

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

คุณแสดงเงื่อนไขได้หลายรายการโดยใช้ else if ซึ่งช่วยให้คุณนำเสนอ ตรรกะที่ซับซ้อนและละเอียดยิ่งขึ้นภายในคำสั่งแบบมีเงื่อนไขเดียว ดังที่แสดงใน ตัวอย่างต่อไปนี้

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

คำสั่งแบบมีเงื่อนไขมีประโยชน์ในการแสดงตรรกะแบบเก็บสถานะ แต่คุณอาจ จะพบว่าคุณเคยพูดซ้ำเวลาเขียนคำเหล่านั้น ในตัวอย่างข้างต้น คุณ เพียงพิมพ์ String ในแต่ละสาขา เพื่อหลีกเลี่ยงปัญหานี้ Kotlin จึงขอนำเสนอ นิพจน์เงื่อนไข ตัวอย่างสุดท้ายสามารถเขียนใหม่ได้ดังนี้

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

println(answerString)

โดยปริยาย แต่ละ Branch ที่มีเงื่อนไขจะแสดงผลลัพธ์ของนิพจน์ในหน้า บรรทัดสุดท้าย คุณจึงไม่ต้องใช้คีย์เวิร์ด return เนื่องจากผลลัพธ์ของ ทั้ง 3 Branch เป็นประเภท String ผลลัพธ์ของนิพจน์ if-else คือ และประเภท String ด้วย ในตัวอย่างนี้ answerString ได้รับการกำหนดค่าเริ่มต้น จากผลลัพธ์ของนิพจน์ if-else การอนุมานประเภทสามารถใช้เพื่อ ไม่ใส่การประกาศประเภทที่ชัดเจนสำหรับ answerString แต่ก็มักจะเป็น แนวคิดที่จะรวมข้อความเหล่านั้น เพื่อความชัดเจน

เมื่อข้อความแบบมีเงื่อนไขมีความซับซ้อนมากขึ้น คุณอาจลองพิจารณา แทนที่นิพจน์ if-else ด้วยนิพจน์ when ดังที่แสดงใน ตัวอย่างต่อไปนี้

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

println(answerString)

แต่ละสาขาในนิพจน์ when จะแสดงด้วยเงื่อนไข ซึ่งก็คือลูกศร (->) และผลลัพธ์ หากเงื่อนไขทางด้านซ้ายของลูกศร ประเมินเป็นจริง ผลลัพธ์ของนิพจน์ทางด้านขวาจะเป็น ส่งคืนแล้ว โปรดทราบว่าการดำเนินการจะไม่ตกหล่นจากสาขาหนึ่งไปยังสาขาถัดไป โค้ดในตัวอย่างนิพจน์ when มีฟังก์ชันการทำงานเทียบเท่ากับโค้ดใน จากตัวอย่างก่อนหน้านี้ แต่น่าจะอ่านได้ง่ายกว่า

เงื่อนไขของ Kotlin ไฮไลต์ฟีเจอร์ที่มีประสิทธิภาพมากกว่านั้น การแคสต์อัจฉริยะ แทนที่จะใช้ตัวดำเนินการ Safe-Call หรือ "ไม่ใช่ค่าว่าง" คุณสามารถตรวจสอบว่า มีการอ้างอิงไปยังค่า Null โดยใช้คำสั่งแบบมีเงื่อนไข เช่น ที่แสดงในตัวอย่างต่อไปนี้

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

ภายใน Branch แบบมีเงื่อนไข ระบบอาจถือว่า languageName เป็นค่าว่าง Kotlin ฉลาดพอที่จะรับรู้ว่ามีเงื่อนไขสำหรับการดำเนินการกับ Branch คือ languageName ไม่ได้เก็บค่า Null คุณจึงไม่ต้องใช้ languageName เป็นค่าว่างภายในสาขานั้น การแคสต์อัจฉริยะนี้ใช้งานได้สำหรับ null เช็ค การตรวจสอบประเภท หรือเงื่อนไขใดๆ ที่เป็นไปตาม สัญญา

ฟังก์ชัน

คุณจะจัดกลุ่มนิพจน์ตั้งแต่ 1 รายการขึ้นไปลงในฟังก์ชันได้ แทนที่จะทำซ้ำ ชุดนิพจน์เดียวกันทุกครั้งที่คุณต้องการผลลัพธ์ คุณสามารถตัด นิพจน์ในฟังก์ชัน และเรียกใช้ฟังก์ชันนั้นแทน

หากต้องการประกาศฟังก์ชัน ให้ใช้คีย์เวิร์ด fun ตามด้วยชื่อฟังก์ชัน ถัดไป ให้กำหนดประเภทอินพุตที่ฟังก์ชันใช้ (หากมี) แล้วประกาศ ประเภทของเอาต์พุตที่แสดงผล ส่วนเนื้อหาของฟังก์ชันคือตำแหน่งที่คุณกำหนด นิพจน์ที่เรียกใช้เมื่อเรียกใช้ฟังก์ชัน

ฟังก์ชัน Kotlin ที่สมบูรณ์จากตัวอย่างก่อนหน้านี้มีดังนี้

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

    return answerString
}

ฟังก์ชันในตัวอย่างด้านบนมีชื่อ generateAnswerString ทั้งนี้ จะไม่ป้อนข้อมูลใดๆ โดยเอาต์พุตจะเป็นผลลัพธ์ประเภท String หากต้องการโทรหา ให้ใช้ชื่อฟังก์ชัน ตามด้วยโอเปอเรเตอร์คำขอ (()) ใน ในตัวอย่างด้านล่าง ตัวแปร answerString เริ่มต้นด้วยผลลัพธ์จาก generateAnswerString()

val answerString = generateAnswerString()

ฟังก์ชันรับอาร์กิวเมนต์เป็นอินพุตได้ ดังที่ปรากฏในตัวอย่างต่อไปนี้

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

    return answerString
}

เมื่อประกาศฟังก์ชัน คุณสามารถระบุจำนวนอาร์กิวเมนต์และอาร์กิวเมนต์ ประเภทต่างๆ ในตัวอย่างด้านบน generateAnswerString() รับอาร์กิวเมนต์ 1 รายการที่ชื่อ countThreshold ประเภท Int ภายในฟังก์ชันนี้ คุณสามารถอ้างอิง โดยใช้ชื่อของอาร์กิวเมนต์

เมื่อเรียกใช้ฟังก์ชันนี้ คุณต้องใส่อาร์กิวเมนต์ภายในฟังก์ชัน วงเล็บของการเรียก:

val answerString = generateAnswerString(42)

การลดความซับซ้อนของการประกาศฟังก์ชัน

generateAnswerString() เป็นฟังก์ชันที่ค่อนข้างง่าย ฟังก์ชันดังกล่าวจะประกาศว่า ตัวแปร แล้วจึงแสดงผลทันที เมื่อผลลัพธ์ของนิพจน์เดียว ที่แสดงผลจากฟังก์ชัน คุณสามารถข้ามการประกาศตัวแปรภายใน ส่งคืนผลลัพธ์ของนิพจน์ if-else ที่มีอยู่ในฟังก์ชัน เช่น ที่แสดงในตัวอย่างต่อไปนี้

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

นอกจากนี้คุณยังแทนที่คีย์เวิร์ดส่งคืนด้วยโอเปอเรเตอร์งานที่กำหนดได้ด้วย โดยทำดังนี้

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

ฟังก์ชันที่ไม่ระบุชื่อ

บางฟังก์ชันไม่จำเป็นต้องมีชื่อ ฟังก์ชันบางอย่างจะระบุโดยตรงมากขึ้นโดย อินพุตและเอาต์พุต ฟังก์ชันเหล่านี้เรียกว่าฟังก์ชันที่ระบุตัวบุคคล คุณ สามารถเก็บการอ้างอิงไปยังฟังก์ชันนิรนาม โดยใช้การอ้างอิงนี้เพื่อ เรียกใช้ฟังก์ชันไม่ระบุตัวตนในภายหลัง นอกจากนี้ คุณยังส่งต่อข้อมูลอ้างอิงเกี่ยวกับ เช่นเดียวกับข้อมูลอ้างอิงประเภทอื่นๆ

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

ฟังก์ชันที่ไม่ระบุชื่อสามารถมีนิพจน์กี่นิพจน์ก็ได้ เช่นเดียวกับฟังก์ชันที่มีชื่อ ค่าที่แสดงผลของฟังก์ชันคือผลลัพธ์ของนิพจน์สุดท้าย

ในตัวอย่างข้างต้น stringLengthFunc มีการอ้างอิงถึงบุคคลนิรนาม ฟังก์ชันที่จะรับ String เป็นอินพุตและแสดงผลความยาวของอินพุต String เป็นเอาต์พุตประเภท Int ด้วยเหตุนี้ ประเภทของฟังก์ชันคือ แสดงเป็น (String) -> Int อย่างไรก็ตาม โค้ดนี้จะไม่เรียกใช้ฟังก์ชัน หากต้องการดึงผลลัพธ์ของฟังก์ชัน คุณจะต้องเรียกใช้ฟังก์ชัน ฟังก์ชันที่มีชื่อ คุณต้องระบุ String เมื่อโทรหา stringLengthFunc เป็น ที่แสดงในตัวอย่างต่อไปนี้

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

val stringLength: Int = stringLengthFunc("Android")

ฟังก์ชันที่มีลำดับสูงกว่า

ฟังก์ชันหนึ่งสามารถใช้ฟังก์ชันอื่นเป็นอาร์กิวเมนต์ได้ ฟังก์ชันที่ใช้อื่นๆ เป็นอาร์กิวเมนต์เรียกว่าฟังก์ชันลำดับสูงกว่า รูปแบบนี้ มีประโยชน์ในการสื่อสารระหว่างองค์ประกอบต่างๆ ในลักษณะเดียวกับที่คุณอาจใช้ Callback ใน Java

ต่อไปนี้คือตัวอย่างของฟังก์ชันที่มีลำดับสูงกว่า

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

ฟังก์ชัน stringMapper() จะใช้ String พร้อมด้วยฟังก์ชันที่ ดึงค่า Int จาก String ที่คุณส่งเข้าไป

คุณสามารถเรียก stringMapper() ได้โดยการส่ง String และฟังก์ชันที่ เป็นไปตามพารามิเตอร์อินพุตอื่นๆ ซึ่งก็คือฟังก์ชันที่ใช้ค่า String เป็น อินพุตและเอาต์พุต Int ดังที่แสดงในตัวอย่างต่อไปนี้

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

หากฟังก์ชันที่ไม่ระบุตัวตนเป็นพารามิเตอร์สุดท้ายที่กำหนดไว้ในฟังก์ชัน คุณจะดำเนินการต่อไปนี้ได้ ส่งภายนอกวงเล็บที่ใช้ในการเรียกใช้ฟังก์ชัน ดังที่แสดงใน ตัวอย่างต่อไปนี้

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

ฟังก์ชันที่ไม่ระบุชื่อจะอยู่ในไลบรารีมาตรฐานของ Kotlin สำหรับ ข้อมูลเพิ่มเติม โปรดดู ฟังก์ชันลำดับสูงกว่าและ Lambda

ชั้นเรียน

ประเภททั้งหมดที่พูดถึงจนถึงขณะนี้ได้รวมอยู่ในการเขียนโปรแกรม Kotlin แล้ว ภาษา หากต้องการเพิ่มประเภทที่กำหนดเอง คุณสามารถกำหนดชั้นเรียน โดยใช้คีย์เวิร์ด class ดังที่แสดงในตัวอย่างต่อไปนี้

class Car

คุณสมบัติ

ชั้นเรียนจะแสดงสถานะโดยใช้พร็อพเพอร์ตี้ ต property คือ ตัวแปรระดับคลาสที่อาจมี Getter, Setter และ Backing Field เนื่องจากรถยนต์ต้องมีล้อจึงจะขับขี่ คุณเพิ่มรายการวัตถุ Wheel เป็น ของ Car ดังที่แสดงในตัวอย่างต่อไปนี้

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

โปรดทราบว่า wheels เป็น public val ซึ่งหมายความว่า wheels สามารถเข้าถึงได้จาก อยู่นอกชั้นเรียน Car และไม่สามารถเปลี่ยนการมอบหมายได้ ถ้าคุณต้องการขอรับ ของ Car คุณต้องเรียกใช้ตัวสร้างก่อน จากนั้นคุณสามารถ เข้าถึงทุกพร็อพเพอร์ตี้ที่เข้าถึงได้ง่าย

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

หากต้องการปรับแต่งวงล้อ คุณสามารถกำหนดผู้ผลิตเอง ระบุวิธีเริ่มต้นพร็อพเพอร์ตี้คลาส ดังนี้

class Car(val wheels: List<Wheel>)

ในตัวอย่างข้างต้น ตัวสร้างคลาสจะใช้ List<Wheel> เป็น อาร์กิวเมนต์ตัวสร้างและใช้อาร์กิวเมนต์นั้นเพื่อเริ่มต้น wheels

ฟังก์ชันและการห่อหุ้มข้อมูลของชั้นเรียน

ชั้นเรียนใช้ฟังก์ชันเพื่อสร้างโมเดลพฤติกรรม ฟังก์ชันต่างๆ สามารถแก้ไขสถานะ เพื่อแสดงเฉพาะข้อมูลที่คุณต้องการเปิดเผย การควบคุมการเข้าถึงนี้เป็นส่วนหนึ่งของ แนวคิดเชิงวัตถุขนาดใหญ่ที่เรียกว่าการสรุป

ในตัวอย่างต่อไปนี้ พร็อพเพอร์ตี้ doorLock จะถูกเก็บเป็นส่วนตัวจากอะไรก็ได้ นอกชั้นเรียน Car คุณต้องโทรหา unlockDoor() เพื่อปลดล็อกรถ การส่งฟังก์ชันในคีย์ที่ถูกต้อง ดังที่ปรากฏในตัวอย่างต่อไปนี้:

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

    private val doorLock: DoorLock = ...

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

หากต้องการปรับแต่งวิธีอ้างอิงพร็อพเพอร์ตี้ คุณสามารถระบุ Getter และ Setter ที่กำหนดเอง เช่น หากคุณต้องการแสดงพร็อพเพอร์ตี้ getter ขณะจำกัดการเข้าถึงตัวตั้งค่า คุณสามารถระบุตัวตั้งค่านั้นเป็น private:

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

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

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

คุณสร้างคลาสที่ สร้างแบบจำลองวัตถุทุกประเภท

ความสามารถในการทำงานร่วมกัน

ฟีเจอร์ที่สำคัญที่สุดอย่างหนึ่งของ Kotlin คือความสามารถในการทํางานร่วมกันแบบไหลกับ Java เนื่องจากโค้ด Kotlin คอมไพล์ลงไปเป็นไบต์โค้ด JVM โค้ด Kotlin จึงสามารถเรียกใช้ ลงในโค้ด Java โดยตรง หรือกลับกัน ซึ่งหมายความว่าคุณสามารถใช้ประโยชน์ ไลบรารี Java ที่มีอยู่แล้ว โดยตรงจาก Kotlin นอกจากนี้ ส่วนใหญ่ของ API ของ Android เขียนขึ้นใน Java ซึ่งคุณจะเรียกใช้ได้โดยตรงจาก Kotlin

ขั้นตอนถัดไป

Kotlin เป็นภาษาที่มีความยืดหยุ่นและใช้งานได้จริง ซึ่งมีการสนับสนุนและแรงกระตุ้นที่เพิ่มขึ้นเรื่อยๆ พ เราขอแนะนำให้คุณลองหากคุณยังไม่ได้ทำ ขั้นตอนถัดไป โปรดดูที่ ในเอกสารประกอบของ Kotlin อย่างเป็นทางการ รวมถึงคำแนะนำเกี่ยวกับวิธีการใช้ รูปแบบ Kotlin ทั่วไปในแอป Android