0Pricing
Kotlin Academy · บทเรียน

Star Projection และการใช้ *

ใช้ star projection กับชนิด generic ที่ไม่ทราบแน่ชัดและทำความเข้าใจข้อจำกัด

Star Projection และการใช้ * เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

การฉายแบบดอกจันคืออะไร

การฉายแบบดอกจัน (*) เป็นวิธีของ Kotlin ในการระบุว่าเป็นชนิดข้อมูลเจเนอริกที่ไม่ทราบอาร์กิวเมนต์ชนิดข้อมูล เป็นทางเลือกที่ปลอดภัยเทียบเท่ากับชนิดดิบหรือไวลด์การ์ดไร้ขอบเขตของ Java <?>

fun printList(list: List<*>) {
    for (item in list) println(item)
}
fun main() {
    printList(listOf(1, 2, 3))
    printList(listOf("a", "b"))
    printList(listOf(true, 42, "mixed"))
}

การอ่านจากชนิดที่ฉายแบบดอกจัน

จาก List<*> คุณสามารถอ่านสมาชิกเป็น Any? ได้ แต่ไม่สามารถเพิ่มสมาชิกได้ เพราะไม่ทราบชนิดข้อมูลจริง

fun process(list: List<*>) {
    val first: Any? = list.firstOrNull()
    println("First: $first, Size: ${list.size}")
    // list.add("x") // compile error: can't write to List<*>
}
fun main() {
    process(listOf(10, 20, 30))
}

ดอกจันเทียบกับ out Any

List<*> และ List<out Any?> มีความหมายเทียบเท่ากัน การฉายแบบดอกจันเป็นรูปแบบย่อของการฉายแบบมีความแปรผันร่วมที่ใช้ขอบเขตบน

fun sumStars(list: List<*>): Int = list.size
fun sumOut(list: List<out Any?>): Int = list.size
fun main() {
    val l = listOf(1, "two", true)
    println(sumStars(l))  // 3
    println(sumOut(l))    // 3
}

ดอกจันในพารามิเตอร์ฟังก์ชัน

ใช้ * ในลายเซ็นฟังก์ชันเมื่อต้องการรับรูปแบบเจเนอริกใด ๆ แต่จะเรียกใช้ได้เฉพาะเมธอดที่ไม่จำเป็นต้องทราบ T

fun containerInfo(map: Map<*, *>): String {
    return "keys=${map.keys.size}, values=${map.values.size}"
}
fun main() {
    val m = mapOf(1 to "one", 2 to "two")
    println(containerInfo(m))
}

ดอกจันกับ MutableList

สำหรับ MutableList<*> คุณสามารถอ่านค่าเป็น Any? ได้ แต่ไม่สามารถเขียนได้เลย คอมไพเลอร์จะป้องกันการกระทำดังกล่าว

fun clearFirstElement(list: MutableList<*>) {
    if (list.isNotEmpty()) {
        list.removeAt(0)  // OK: remove by index doesn't need T
        // list.add("x") // Error: can't write unknown type
    }
}
fun main() {
    val m = mutableListOf(1, 2, 3)
    clearFirstElement(m)
    println(m) // [2, 3]
}

คลาสที่มีการฉายแบบดอกจัน

เมื่อตรวจสอบชนิดของคลาสเจเนอริกขณะรันไทม์ ให้ใช้การฉายแบบดอกจัน เพราะอาร์กิวเมนต์ชนิดข้อมูลจะถูกลบออก

fun isListOfSomething(obj: Any): Boolean {
    return obj is List<*>  // OK with star
    // obj is List<String> // Warning: cannot check erased type
}
fun main() {
    println(isListOfSomething(listOf(1, 2)))  // true
    println(isListOfSomething("not a list"))  // false
}

พารามิเตอร์ดอกจันหลายรายการ

ฟังก์ชันสามารถมีพารามิเตอร์ที่ฉายแบบดอกจันหลายรายการได้ เมื่อต้องทำงานกับคู่หรือแมปที่มีชนิดข้อมูลไม่ทราบแน่ชัด

fun describeMap(map: Map<*, *>) {
    map.forEach { (k, v) ->
        println("${k?.javaClass?.simpleName} -> ${v?.javaClass?.simpleName}")
    }
}
fun main() {
    describeMap(mapOf("key" to 42, 1 to true))
}

กรณีที่ไม่ควรใช้ดอกจัน

หลีกเลี่ยงการฉายแบบดอกจันเมื่อต้องเขียนลงในคอลเลกชัน หรือเมื่อสามารถใช้พารามิเตอร์ชนิดข้อมูลที่มีขอบเขตแทนได้ เพราะแบบมีขอบเขตให้ข้อมูลมากกว่า

// Bad: loses type information
fun addToStar(list: MutableList<*>, item: Any?) {
    // list.add(item) // compile error
}
// Better: use bounded type parameter
fun <T : Any> addTyped(list: MutableList<T>, item: T) {
    list.add(item)
}
fun main() {
    val l = mutableListOf("a")
    addTyped(l, "b")
    println(l)
}

ดอกจันกับการตรวจสอบชนิดและการแปลงชนิด

ใช้การฉายแบบดอกจันเพื่อตรวจสอบชนิดของคลาสเจเนอริกด้วยการตรวจสอบอินสแตนซ์ จากนั้นจึงแปลงเป็นชนิดข้อมูลเฉพาะเพื่อใช้งานจริง

fun handleResponse(response: Any) {
    if (response is List<*>) {
        val list = response as List<*>
        println("List with ${list.size} elements")
        val first = list.firstOrNull()
        if (first is String) println("First string: $first")
    }
}
fun main() {
    handleResponse(listOf("hello", "world"))
    handleResponse(42)
}

ภาคปฏิบัติ: ตัวกระจายเหตุการณ์แบบเจเนอริก

ตัวกระจายเหตุการณ์ที่รับ Handler<*> ใด ๆ ได้ แต่ยังกระจายเหตุการณ์ที่มีชนิดข้อมูลได้อย่างปลอดภัย

interface Handler<in T> { fun handle(event: T) }
class LogHandler : Handler<Any> {
    override fun handle(event: Any) = println("Event: $event")
}
fun dispatch(handler: Handler<*>, event: Any) {
    @Suppress("UNCHECKED_CAST")
    (handler as Handler<Any>).handle(event)
}
fun main() {
    dispatch(LogHandler(), "click")
    dispatch(LogHandler(), 42)
}

ดอกจันในบริบท reified

การฉายแบบดอกจันพบได้บ่อยเป็นพิเศษในโค้ดรีเฟลกชันและการซีเรียลไลซ์ ซึ่งมีการตรวจสอบชนิดข้อมูลขณะรันไทม์ผ่าน KClass<*>

fun printClassName(klass: kotlin.reflect.KClass<*>) {
    println(klass.simpleName)
}
fun main() {
    printClassName(String::class)     // String
    printClassName(List::class)       // List
    printClassName(Int::class)        // Int
}

ตรวจสอบความเข้าใจ

เมื่ออ่านค่าจาก List<*> คุณจะได้ชนิดข้อมูลใด

สรุปทบทวน

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

คำถามที่พบบ่อย

บทเรียน “Star Projection และการใช้ *” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “Star Projection และการใช้ *” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “Star Projection และการใช้ *”

ใช้ star projection กับชนิด generic ที่ไม่ทราบแน่ชัดและทำความเข้าใจข้อจำกัด คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “Star Projection และการใช้ *” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ฟังก์ชัน Generic และข้อจำกัดชนิดด้วย where
  2. ความแปรผัน ณ จุดประกาศ: in และ out
  3. Star Projection และการใช้ *
  4. การลบชนิดและพารามิเตอร์ชนิด reified
← กลับไปที่ Kotlin Academy