Kotlin Bootcamp สำหรับโปรแกรมเมอร์ 6: การจัดการฟังก์ชัน

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

บทนำ

นี่คือโค้ดแล็บสุดท้ายใน Kotlin Bootcamp ในโค้ดแล็บนี้ คุณจะได้เรียนรู้เกี่ยวกับคำอธิบายประกอบและช่วงพักที่มีป้ายกำกับ คุณจะตรวจสอบแลมบ์ดาและฟังก์ชันลำดับสูง ซึ่งเป็นส่วนสำคัญของ Kotlin นอกจากนี้ คุณยังได้เรียนรู้เพิ่มเติมเกี่ยวกับฟังก์ชันอินไลน์และอินเทอร์เฟซ Single Abstract Method (SAM) สุดท้ายนี้ คุณจะได้ดูข้อมูลเพิ่มเติมเกี่ยวกับคลังมาตรฐานของ Kotlin

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

สิ่งที่คุณควรทราบอยู่แล้ว

  • ไวยากรณ์ของฟังก์ชัน คลาส และเมธอด Kotlin
  • วิธีสร้างคลาสใหม่ใน IntelliJ IDEA และเรียกใช้โปรแกรม
  • ข้อมูลพื้นฐานเกี่ยวกับฟังก์ชันแลมบ์ดาและฟังก์ชันลำดับสูง

สิ่งที่คุณจะได้เรียนรู้

  • ข้อมูลเบื้องต้นเกี่ยวกับคำอธิบายประกอบ
  • วิธีใช้การแบ่งที่มีป้ายกำกับ
  • ข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันลำดับที่สูงกว่า
  • เกี่ยวกับอินเทอร์เฟซ Single Abstract Method (SAM)
  • เกี่ยวกับไลบรารีมาตรฐานของ Kotlin

สิ่งที่คุณต้องดำเนินการ

  • สร้างคำอธิบายประกอบแบบง่าย
  • ใช้การแบ่งที่มีป้ายกำกับ
  • ตรวจสอบฟังก์ชัน Lambda ใน Kotlin
  • ใช้และสร้างฟังก์ชันลำดับที่สูงกว่า
  • เรียกใช้อินเทอร์เฟซ Single Abstract Method บางรายการ
  • ใช้ฟังก์ชันบางอย่างจาก Kotlin Standard Library

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

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

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

ตัวอย่างคำอธิบายประกอบมีดังนี้

@file:JvmName("InteropFish")
class InteropFish {
   companion object {
       @JvmStatic fun interop()
   }
}

ข้อความนี้ระบุว่าชื่อที่ส่งออกของไฟล์นี้คือ InteropFish พร้อมคำอธิบายประกอบ JvmName โดยคำอธิบายประกอบ JvmName จะรับอาร์กิวเมนต์เป็น "InteropFish" ในออบเจ็กต์คู่ @JvmStatic จะบอกให้ Kotlin สร้าง interop() เป็นฟังก์ชันแบบคงที่ใน InteropFish

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

ขั้นตอนที่ 1: สร้างแพ็กเกจและไฟล์ใหม่

  1. สร้างแพ็กเกจใหม่ example ในส่วน src
  2. ใน example ให้สร้างไฟล์ Kotlin ใหม่ชื่อ Annotations.kt

ขั้นตอนที่ 2: สร้างคำอธิบายประกอบของคุณเอง

  1. ใน Annotations.kt ให้สร้างPlantชั้นเรียนด้วย 2 วิธี ได้แก่ trim() และ fertilize()
class Plant {
        fun trim(){}
        fun fertilize(){}
}
  1. สร้างฟังก์ชันที่พิมพ์เมธอดทั้งหมดในคลาส ใช้ ::class เพื่อรับข้อมูลเกี่ยวกับคลาสในขณะรันไทม์ ใช้ declaredMemberFunctions เพื่อดูรายการเมธอดของคลาส (หากต้องการเข้าถึงส่วนนี้ คุณต้องนำเข้า kotlin.reflect.full.*)
import kotlin.reflect.full.*    // required import

class Plant {
    fun trim(){}
    fun fertilize(){}
}

fun testAnnotations() {
    val classObj = Plant::class
    for (m in classObj.declaredMemberFunctions) {
        println(m.name)
    }
}
  1. สร้างmain()ฟังก์ชันเพื่อเรียกใช้กิจวัตรการทดสอบ เรียกใช้โปรแกรมและสังเกตเอาต์พุต
fun main() {
    testAnnotations()
}
⇒ trim
fertilize
  1. สร้างคำอธิบายประกอบอย่างง่าย ImAPlant
annotation class ImAPlant

ซึ่งจะไม่มีผลใดๆ นอกเหนือจากการระบุว่ามีการอธิบายประกอบ

  1. เพิ่มคำอธิบายประกอบหน้าPlantคลาส
@ImAPlant class Plant{
    ...
}
  1. เปลี่ยน testAnnotations() เพื่อพิมพ์คำอธิบายประกอบทั้งหมดของชั้นเรียน ใช้ annotations เพื่อรับคำอธิบายประกอบทั้งหมดของชั้นเรียน เรียกใช้โปรแกรมและสังเกตผลลัพธ์
fun testAnnotations() {
    val plantObject = Plant::class
    for (a in plantObject.annotations) {
        println(a.annotationClass.simpleName)
    }
}
⇒ ImAPlant
  1. เปลี่ยนtestAnnotations()เพื่อค้นหาคำอธิบายประกอบ ImAPlant ใช้ findAnnotation() เพื่อค้นหาคำอธิบายประกอบที่ต้องการ เรียกใช้โปรแกรมและสังเกตผลลัพธ์
fun testAnnotations() {
    val plantObject = Plant::class
    val myAnnotationObject = plantObject.findAnnotation<ImAPlant>()
    println(myAnnotationObject)
}
⇒ @example.ImAPlant()

ขั้นตอนที่ 3: สร้างคำอธิบายประกอบที่กำหนดเป้าหมาย

คำอธิบายประกอบสามารถกำหนดเป้าหมายไปยัง Getter หรือ Setter ได้ เมื่อมีแล้ว คุณจะใช้ได้โดยมีคำนำหน้าเป็น @get: หรือ @set: ซึ่งมักเกิดขึ้นเมื่อใช้เฟรมเวิร์กที่มีคำอธิบายประกอบ

  1. ประกาศ 2 คำอธิบายประกอบ ได้แก่ OnGet ซึ่งใช้ได้กับตัวรับพร็อพเพอร์ตี้เท่านั้น และ OnSet ซึ่งใช้ได้กับตัวตั้งค่าพร็อพเพอร์ตี้เท่านั้น ใช้ @Target(AnnotationTarger.PROPERTY_GETTER) หรือ PROPERTY_SETTER ในแต่ละรายการ
annotation class ImAPlant

@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class OnGet
@Target(AnnotationTarget.PROPERTY_SETTER)
annotation class OnSet

@ImAPlant class Plant {
    @get:OnGet
    val isGrowing: Boolean = true

    @set:OnSet
    var needsFood: Boolean = false
}

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

Kotlin มีหลายวิธีในการควบคุมโฟลว์ คุณคุ้นเคยกับ return อยู่แล้ว ซึ่งจะส่งคืนจากฟังก์ชันไปยังฟังก์ชันที่ครอบคลุม การใช้ break จะคล้ายกับ return แต่ใช้กับลูป

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

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

  1. ใน Annotations.kt ให้ลองใช้การหยุดที่มีป้ายกำกับโดยการหยุดจากลูปด้านใน
fun labels() {
    outerLoop@ for (i in 1..100) {
         print("$i ")
         for (j in 1..100) {
             if (i > 10) break@outerLoop  // breaks to outer loop
        }
    }
}

fun main() {
    labels()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ 1 2 3 4 5 6 7 8 9 10 11 

ในทำนองเดียวกัน คุณสามารถใช้ continue ที่ติดป้ายกำกับได้ แทนที่จะออกจากลูปที่มีป้ายกำกับ คำสั่ง continue ที่มีป้ายกำกับจะไปยังการวนซ้ำถัดไปของลูป

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

ขั้นตอนที่ 1: สร้าง Lambda อย่างง่าย

  1. เริ่ม REPL ใน IntelliJ IDEA โดยไปที่Tools > Kotlin > Kotlin REPL
  2. สร้าง Lambda ที่มีอาร์กิวเมนต์ dirty: Int ซึ่งทำการคำนวณโดยหาร dirty ด้วย 2 กำหนดฟังก์ชัน Lambda ให้กับตัวแปร waterFilter
val waterFilter = { dirty: Int -> dirty / 2 }
  1. โทรหา waterFilter โดยส่งค่า 30
waterFilter(30)
⇒ res0: kotlin.Int = 15

ขั้นตอนที่ 2: สร้าง Lambda ตัวกรอง

  1. สร้างคลาสข้อมูล Fish ที่มีพร็อพเพอร์ตี้ name ใน REPL
data class Fish(val name: String)
  1. สร้างรายการFish 3 รายการที่มีชื่อว่า Flipper, Moby Dick และ Dory
val myFish = listOf(Fish("Flipper"), Fish("Moby Dick"), Fish("Dory"))
  1. เพิ่มตัวกรองเพื่อตรวจสอบชื่อที่มีตัวอักษร "i"
myFish.filter { it.name.contains("i")}
⇒ res3: kotlin.collections.List<Line_1.Fish> = [Fish(name=Flipper), Fish(name=Moby Dick)]

ในนิพจน์ Lambda it หมายถึงองค์ประกอบรายการปัจจุบัน และระบบจะใช้ตัวกรองกับองค์ประกอบรายการแต่ละรายการตามลำดับ

  1. ใช้ joinString() กับผลลัพธ์โดยใช้ ", " เป็นตัวคั่น
myFish.filter { it.name.contains("i")}.joinToString(", ") { it.name }
⇒ res4: kotlin.String = Flipper, Moby Dick

ฟังก์ชัน joinToString() จะสร้างสตริงโดยการรวมชื่อที่กรองแล้ว ซึ่งคั่นด้วยสตริงที่ระบุ ซึ่งเป็นหนึ่งในฟังก์ชันที่มีประโยชน์มากมายที่สร้างขึ้นในไลบรารีมาตรฐานของ Kotlin

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

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

ขั้นตอนที่ 1: สร้างชั้นเรียนใหม่

  1. สร้างไฟล์ Kotlin ใหม่ Fish.kt ในแพ็กเกจ example
  2. ใน Fish.kt ให้สร้างคลาสข้อมูล Fish ที่มีพร็อพเพอร์ตี้ 1 รายการ name
data class Fish (var name: String)
  1. สร้างฟังก์ชัน fishExamples() ใน fishExamples() ให้สร้างปลาชื่อ "splashy" โดยใช้ตัวพิมพ์เล็กทั้งหมด
fun fishExamples() {
    val fish = Fish("splashy")  // all lowercase
}
  1. สร้างฟังก์ชัน main() ที่เรียกใช้ fishExamples()
fun main () {
    fishExamples()
}
  1. คอมไพล์และเรียกใช้โปรแกรมโดยคลิกสามเหลี่ยมสีเขียวทางด้านซ้ายของ main() ยังไม่มีเอาต์พุต

ขั้นตอนที่ 2: ใช้ฟังก์ชันลำดับที่สูงกว่า

ฟังก์ชัน with() ช่วยให้คุณอ้างอิงออบเจ็กต์หรือพร็อพเพอร์ตี้อย่างน้อย 1 รายการได้ในรูปแบบที่กะทัดรัดยิ่งขึ้น ใช้ this with() เป็นฟังก์ชันลำดับที่สูงกว่า และใน Lambda คุณจะระบุสิ่งที่ต้องทำกับออบเจ็กต์ที่ระบุ

  1. ใช้ with() เพื่อเขียนชื่อปลาเป็นตัวพิมพ์ใหญ่ใน fishExamples() ภายในเครื่องหมายปีกกา this หมายถึงออบเจ็กต์ที่ส่งไปยัง with()
fun fishExamples() {
    val fish = Fish("splashy")  // all lowercase
    with (fish.name) {
        this.capitalize()
    }
}
  1. ไม่มีเอาต์พุต ดังนั้นให้เพิ่ม println() รอบๆ และ this เป็นแบบโดยนัยและไม่จำเป็น คุณจึงนำออกได้
fun fishExamples() {
    val fish = Fish("splashy")  // all lowercase
    with (fish.name) {
        println(capitalize())
    }
}
⇒ Splashy

ขั้นตอนที่ 3: สร้างฟังก์ชันลำดับสูง

with() เป็นฟังก์ชันลำดับสูง หากต้องการดูวิธีการทำงานนี้ คุณสามารถสร้างเวอร์ชันที่เรียบง่ายของ with() ที่ใช้ได้กับสตริงเท่านั้น

  1. ใน Fish.kt ให้กำหนดฟังก์ชัน myWith() ที่รับอาร์กิวเมนต์ 2 รายการ อาร์กิวเมนต์คือออบเจ็กต์ที่จะดำเนินการ และฟังก์ชันที่กำหนดการดำเนินการ รูปแบบสำหรับชื่ออาร์กิวเมนต์ที่มีฟังก์ชันคือ block ในกรณีนี้ ฟังก์ชันดังกล่าวจะไม่แสดงผลใดๆ ซึ่งระบุด้วย Unit
fun myWith(name: String, block: String.() -> Unit) {}

ตอนนี้ block() เป็นฟังก์ชันส่วนขยายของ String ใน myWith() แล้ว โดยมักเรียกคลาสที่ขยายว่าออบเจ็กต์ตัวรับ ดังนั้น name จึงเป็นออบเจ็กต์ผู้รับในกรณีนี้

  1. ในส่วนเนื้อหาของ myWith() ให้ใช้ฟังก์ชัน block() ที่ส่งเข้ามากับออบเจ็กต์ตัวรับ name
fun myWith(name: String, block: String.() -> Unit) {
    name.block()
}
  1. ใน fishExamples() ให้แทนที่ with() ด้วย myWith()
fun fishExamples() {
    val fish = Fish("splashy")  // all lowercase
    myWith (fish.name) {
        println(capitalize())
    }
}

fish.name คืออาร์กิวเมนต์ชื่อ และ println(capitalize()) คือฟังก์ชันบล็อก

  1. เรียกใช้โปรแกรม แล้วโปรแกรมจะทำงานเหมือนเดิม
⇒ Splashy

ขั้นตอนที่ 4: สำรวจส่วนขยายอื่นๆ ที่มีอยู่

with() Lambda ของส่วนขยายมีประโยชน์มากและเป็นส่วนหนึ่งของไลบรารีมาตรฐานของ Kotlin และยังมีอีกหลายอย่างที่คุณอาจพบว่ามีประโยชน์ เช่น run(), apply() และ let()

ฟังก์ชัน run() เป็นส่วนขยายที่ใช้ได้กับทุกประเภท โดยจะรับ lambda เป็นอาร์กิวเมนต์ 1 รายการและแสดงผลลัพธ์ของการเรียกใช้ lambda

  1. ใน fishExamples() โทรหา run() ในวันที่ fish เพื่อขอชื่อ
fish.run {
   name
}

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

ฟังก์ชัน apply() คล้ายกับ run() แต่จะแสดงผลออบเจ็กต์ที่เปลี่ยนแปลงซึ่งใช้กับฟังก์ชันดังกล่าวแทนที่จะเป็นผลลัพธ์ของ Lambda ซึ่งอาจเป็นประโยชน์สำหรับการเรียกใช้เมธอดในออบเจ็กต์ที่สร้างขึ้นใหม่

  1. ทำสำเนาของ fish และเรียกใช้ apply() เพื่อตั้งชื่อสำเนาใหม่
val fish2 = Fish(name = "splashy").apply {
     name = "sharky"
}
println(fish2.name)
⇒ sharky

ฟังก์ชัน let() คล้ายกับ apply() แต่จะแสดงผลสำเนาของออบเจ็กต์ที่มีการเปลี่ยนแปลง ซึ่งอาจมีประโยชน์ในการเชื่อมโยงการปรับแต่งเข้าด้วยกัน

  1. ใช้ let() เพื่อรับชื่อของ fish, ทำให้เป็นตัวพิมพ์ใหญ่, ต่อสตริงอื่นเข้ากับสตริงนั้น, รับความยาวของผลลัพธ์นั้น, เพิ่ม 31 ลงในความยาว แล้วพิมพ์ผลลัพธ์
println(fish.let { it.name.capitalize()}
.let{it + "fish"}
.let{it.length}
.let{it + 31})
⇒ 42

ในตัวอย่างนี้ ประเภทออบเจ็กต์ที่ it อ้างอิงคือ Fish จากนั้นคือ String แล้วก็ String อีกครั้ง และสุดท้ายคือ Int

  1. พิมพ์ fish หลังจากเรียกใช้ let() แล้วคุณจะเห็นว่าค่าไม่เปลี่ยนแปลง
println(fish.let { it.name.capitalize()}
    .let{it + "fish"}
    .let{it.length}
    .let{it + 31})
println(fish)
⇒ 42
Fish(name=splashy)

Lambda และฟังก์ชันลำดับสูงมีประโยชน์มาก แต่คุณควรทราบว่า Lambda คือออบเจ็กต์ นิพจน์ Lambda เป็นอินสแตนซ์ของอินเทอร์เฟซ Function ซึ่งเป็นชนิดย่อยของ Object ลองดูตัวอย่าง myWith() ก่อนหน้านี้

myWith(fish.name) {
    capitalize()
}

Function อินเทอร์เฟซมีเมธอด invoke() ซึ่งจะมีการลบล้างเพื่อเรียกนิพจน์ Lambda หากเขียนเป็นคำพูดแบบยาวๆ จะมีลักษณะคล้ายกับโค้ดด้านล่าง

// actually creates an object that looks like this
myWith(fish.name, object : Function1<String, Unit> {
    override fun invoke(name: String) {
        name.capitalize()
    }
})

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

Kotlin มี inline เป็นวิธีจัดการกรณีนี้เพื่อลดค่าใช้จ่ายในช่วงรันไทม์โดยเพิ่มงานอีกเล็กน้อยให้กับคอมไพเลอร์ (คุณได้เรียนรู้เกี่ยวกับ inline มาบ้างแล้วในบทเรียนก่อนหน้านี้ที่พูดถึงประเภทที่ทำให้เป็นจริง) การทำเครื่องหมายฟังก์ชันเป็น inline หมายความว่าทุกครั้งที่มีการเรียกใช้ฟังก์ชัน คอมไพเลอร์จะแปลงซอร์สโค้ดเป็น "อินไลน์" ฟังก์ชัน กล่าวคือ คอมไพเลอร์จะเปลี่ยนโค้ดเพื่อแทนที่ Lambda ด้วยคำสั่งภายใน Lambda

หาก myWith() ในตัวอย่างข้างต้นมีเครื่องหมาย inline

inline myWith(fish.name) {
    capitalize()
}

จะเปลี่ยนเป็นการเรียกใช้โดยตรงดังนี้

// with myWith() inline, this becomes
fish.name.capitalize()

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

Single Abstract Method หมายถึงอินเทอร์เฟซที่มีเมธอดเดียว ซึ่งพบบ่อยมากเมื่อใช้ API ที่เขียนด้วยภาษาโปรแกรม Java จึงมีตัวย่อสำหรับคำนี้คือ SAM ตัวอย่างเช่น Runnable ซึ่งมีเมธอดนามธรรมเดียวคือ run() และ Callable ซึ่งมีเมธอดนามธรรมเดียวคือ call()

ใน Kotlin คุณต้องเรียกใช้ฟังก์ชันที่ใช้ SAM เป็นพารามิเตอร์อยู่เสมอ ลองดูตัวอย่างด้านล่าง

  1. สร้างคลาส Java JavaRun ภายใน example แล้ววางข้อมูลต่อไปนี้ลงในไฟล์
package example;

public class JavaRun {
    public static void runNow(Runnable runnable) {
        runnable.run();
    }
}

Kotlin ช่วยให้คุณสร้างออบเจ็กต์ที่ใช้การเชื่อมต่อได้โดยนำหน้าประเภทด้วย object: ซึ่งมีประโยชน์ในการส่งพารามิเตอร์ไปยัง SAM

  1. กลับไปที่ Fish.kt สร้างฟังก์ชัน runExample() ซึ่งสร้าง Runnable โดยใช้ object: ออบเจ็กต์ควรใช้ run() โดยพิมพ์ "I'm a Runnable"
fun runExample() {
    val runnable = object: Runnable {
        override fun run() {
            println("I'm a Runnable")
        }
    }
}
  1. เรียกใช้ JavaRun.runNow() ด้วยออบเจ็กต์ที่คุณสร้าง
fun runExample() {
    val runnable = object: Runnable {
        override fun run() {
            println("I'm a Runnable")
        }
    }
    JavaRun.runNow(runnable)
}
  1. โทรหา runExample() จาก main() แล้วเรียกใช้โปรแกรม
⇒ I'm a Runnable

การพิมพ์ต้องใช้ความพยายามอย่างมาก แต่ก็เป็นตัวอย่างที่ดีของวิธีการทำงานของ SAM แน่นอนว่า Kotlin มีวิธีที่ง่ายกว่าในการทำเช่นนี้ นั่นคือใช้ Lambda แทนออบเจ็กต์เพื่อให้โค้ดนี้กระชับขึ้นมาก

  1. นำโค้ดที่มีอยู่ออกใน runExample เปลี่ยนให้เรียกใช้ runNow() ด้วย Lambda แล้วเรียกใช้โปรแกรม
fun runExample() {
    JavaRun.runNow({
        println("Passing a lambda as a Runnable")
    })
}
⇒ Passing a lambda as a Runnable
  1. คุณสามารถทำให้โค้ดนี้กระชับยิ่งขึ้นได้โดยใช้ไวยากรณ์การเรียกพารามิเตอร์สุดท้าย และไม่ต้องใส่วงเล็บ
fun runExample() {
    JavaRun.runNow {
        println("Last parameter is a lambda as a Runnable")
    }
}
⇒ Last parameter is a lambda as a Runnable

นั่นคือพื้นฐานของ SAM หรือ Single Abstract Method คุณสามารถสร้างอินสแตนซ์ ลบล้าง และเรียกใช้ SAM ด้วยโค้ดเพียงบรรทัดเดียวโดยใช้รูปแบบ
Class.singleAbstractMethod { lambda_of_override }

บทเรียนนี้ได้ทบทวน Lambda และเจาะลึกฟังก์ชันลำดับสูง ซึ่งเป็นส่วนสำคัญของ Kotlin นอกจากนี้ คุณยังได้เรียนรู้เกี่ยวกับคำอธิบายประกอบและช่วงพักที่มีป้ายกำกับ

  • ใช้คำอธิบายประกอบเพื่อระบุสิ่งต่างๆ ให้คอมไพเลอร์ เช่น
    @file:JvmName("Foo")
  • ใช้การหยุดที่มีป้ายกำกับเพื่อให้โค้ดออกจากลูปที่ซ้อนกัน เช่น
    if (i > 10) break@outerLoop // breaks to outerLoop label
  • Lambda จะมีประสิทธิภาพมากเมื่อใช้ร่วมกับฟังก์ชันลำดับที่สูงกว่า
  • Lambda คือออบเจ็กต์ หากไม่ต้องการสร้างออบเจ็กต์ ให้ทำเครื่องหมายฟังก์ชันด้วย inline แล้วคอมไพเลอร์จะใส่เนื้อหาของ Lambda ลงในโค้ดโดยตรง
  • โปรดใช้ inline อย่างระมัดระวัง แต่จะช่วยลดการใช้ทรัพยากรของโปรแกรมได้
  • SAM (Single Abstract Method) เป็นรูปแบบที่ใช้กันทั่วไป และแลมบ์ดาช่วยให้ใช้งานได้ง่ายขึ้น รูปแบบพื้นฐานคือ
    Class.singleAbstractMethod { lamba_of_override }
  • คลังมาตรฐานของ Kotlin มีฟังก์ชันที่มีประโยชน์มากมาย รวมถึง SAM หลายรายการด้วย ดังนั้นโปรดทำความรู้จักกับสิ่งที่อยู่ในคลังนี้

Kotlin มีอะไรอีกมากมายนอกเหนือจากที่กล่าวถึงในหลักสูตร แต่ตอนนี้คุณก็มีพื้นฐานที่จะเริ่มพัฒนาโปรแกรม Kotlin ของคุณเองแล้ว เราหวังว่าคุณจะตื่นเต้นกับภาษาที่สื่อความหมายนี้ และตั้งตารอที่จะสร้างฟังก์ชันการทำงานเพิ่มเติมในขณะที่เขียนโค้ดน้อยลง (โดยเฉพาะอย่างยิ่งหากคุณมาจากภาษาโปรแกรม Java) การฝึกฝนและเรียนรู้ไปพร้อมๆ กันเป็นวิธีที่ดีที่สุดในการเป็นผู้เชี่ยวชาญด้าน Kotlin ดังนั้นโปรดสำรวจและเรียนรู้เกี่ยวกับ Kotlin ด้วยตนเองต่อไป

เอกสารประกอบ Kotlin

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อใดก็ตามในหลักสูตรนี้ หรือหากคุณติดขัด https://kotlinlang.org คือจุดเริ่มต้นที่ดีที่สุด

บทแนะนำ Kotlin

เว็บไซต์ https://try.kotlinlang.org มีบทแนะนำที่สมบูรณ์ซึ่งเรียกว่า Kotlin Koans, ตัวแปลภาษาบนเว็บ และชุดเอกสารอ้างอิงที่สมบูรณ์พร้อมตัวอย่าง

หลักสูตร Udacity

หากต้องการดูหลักสูตร Udacity ในหัวข้อนี้ โปรดดูค่ายฝึก Kotlin สำหรับโปรแกรมเมอร์

IntelliJ IDEA

เอกสารประกอบสำหรับ IntelliJ IDEA อยู่ในเว็บไซต์ของ JetBrains

ไลบรารีมาตรฐานของ Kotlin

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

บทแนะนำ Kotlin

เว็บไซต์ https://try.kotlinlang.org มีบทแนะนำที่สมบูรณ์ซึ่งเรียกว่า Kotlin Koans, ตัวแปลภาษาบนเว็บ และชุดเอกสารอ้างอิงที่สมบูรณ์พร้อมตัวอย่าง

หลักสูตร Udacity

หากต้องการดูหลักสูตร Udacity ในหัวข้อนี้ โปรดดูค่ายฝึก Kotlin สำหรับโปรแกรมเมอร์

IntelliJ IDEA

เอกสารประกอบสำหรับ IntelliJ IDEA อยู่ในเว็บไซต์ของ JetBrains

ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้

  • มอบหมายการบ้านหากจำเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
  • ให้คะแนนงานการบ้าน

ผู้สอนสามารถใช้คำแนะนำเหล่านี้ได้มากน้อยตามที่ต้องการ และควรมีอิสระในการมอบหมายการบ้านอื่นๆ ที่เห็นว่าเหมาะสม

หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ

ตอบคำถามต่อไปนี้

คำถามที่ 1

ใน Kotlin คำว่า SAM ย่อมาจาก

▢ การจับคู่อาร์กิวเมนต์ที่ปลอดภัย

▢ วิธีการเข้าถึงแบบง่าย

▢ Single Abstract Method

▢ วิธีการเข้าถึงเชิงกลยุทธ์

คำถามที่ 2

ข้อใดต่อไปนี้ไม่ใช่ฟังก์ชันส่วนขยายของ Kotlin Standard Library

elvis()

apply()

run()

with()

คำถามที่ 3

ข้อใดต่อไปนี้ไม่เป็นจริงเกี่ยวกับ Lambda ใน Kotlin

▢ แลมบ์ดาคือฟังก์ชันที่ไม่ระบุชื่อ

▢ แลมบ์ดาคือออบเจ็กต์ เว้นแต่จะมีการแทรกในบรรทัด

▢ Lambda ใช้ทรัพยากรมากและไม่ควรใช้

▢ ส่ง Lambda ไปยังฟังก์ชันอื่นๆ ได้

คำถามที่ 4

ป้ายกำกับใน Kotlin จะระบุด้วยตัวระบุตามด้วย

:

::

@:

@

ยินดีด้วย คุณทำ Codelab หลักสูตรติวเข้ม Kotlin สำหรับโปรแกรมเมอร์เสร็จสมบูรณ์แล้ว

ดูภาพรวมของหลักสูตร รวมถึงลิงก์ไปยังโค้ดแล็บอื่นๆ ได้ที่ "Kotlin Bootcamp for Programmers: Welcome to the course"