Kotlin: วิธีใช้งาน List casts: Unchecked Cast: kotlin.collections.List <Kotlin.Any?> ไปยัง kotlin.colletions.List <Waypoint>


108

ฉันต้องการเขียนฟังก์ชันที่ส่งคืนทุกรายการในรายการListที่ไม่ใช่รายการแรกหรือรายการสุดท้าย (a via point) ฟังก์ชันได้รับข้อมูลทั่วไปList<*>เป็นอินพุต ควรส่งคืนผลลัพธ์ก็ต่อเมื่อองค์ประกอบของรายการเป็นประเภทWaypoint:

fun getViaPoints(list: List<*>): List<Waypoint>? {

    list.forEach { if(it !is Waypoint ) return null }

    val waypointList = list as? List<Waypoint> ?: return null

    return waypointList.filter{ waypointList.indexOf(it) != 0 && waypointList.indexOf(it) != waypointList.lastIndex}
}

เมื่อส่งList<*>ไปList<Waypoint>ฉันได้รับคำเตือน:

Unchecked Cast: kotlin.collections.List to kotlin.colletions.List

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

คำตอบ:


192

ใน Kotlin ไม่มีวิธีตรวจสอบพารามิเตอร์ทั่วไปที่รันไทม์ในกรณีทั่วไป (เช่นการตรวจสอบรายการของ a List<T>ซึ่งเป็นกรณีพิเศษเท่านั้น) ดังนั้นการส่งประเภททั่วไปไปยังอีกประเภทหนึ่งด้วยพารามิเตอร์ทั่วไปที่แตกต่างกันจะส่งคำเตือนเว้นแต่ หล่ออยู่ภายในขอบเขตความแปรปรวน

มีวิธีแก้ไขที่แตกต่างกันอย่างไรก็ตาม:

  • คุณได้ตรวจสอบประเภทและคุณค่อนข้างแน่ใจว่านักแสดงปลอดภัย ระบุว่าคุณสามารถปราบปรามการเตือน@Suppress("UNCHECKED_CAST")ด้วย

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
  • ใช้.filterIsInstance<T>()ฟังก์ชันซึ่งตรวจสอบประเภทรายการและส่งคืนรายการที่มีรายการประเภทที่ผ่าน:

    val waypointList: List<Waypoint> = list.filterIsInstance<Waypoint>()
    
    if (waypointList.size != list.size)
        return null

    หรือเหมือนกันในคำสั่งเดียว:

    val waypointList = list.filterIsInstance<Waypoint>()
        .apply { if (size != list.size) return null }

    สิ่งนี้จะสร้างรายการใหม่ของประเภทที่ต้องการ (ดังนั้นจึงหลีกเลี่ยงการร่ายที่ไม่ถูกตรวจสอบภายใน) แนะนำค่าใช้จ่ายเล็กน้อย แต่ในขณะเดียวกันก็ช่วยให้คุณประหยัดจากการวนซ้ำlistและตรวจสอบประเภท (ในlist.foreach { ... }บรรทัด) ดังนั้นจึงไม่เป็นเช่นนั้น เห็นได้ชัด

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

    @Suppress("UNCHECKED_CAST")
    inline fun <reified T : Any> List<*>.checkItemsAre() =
            if (all { it is T })
                this as List<T>
            else null

    ด้วยการใช้งาน:

    val waypointList = list.checkItemsAre<Waypoint>() ?: return null

6
คำตอบที่ดี! ฉันเลือกโซลูชัน list.filterIsInstance <aypoint> () เพราะฉันคิดว่าเป็นโซลูชันที่สะอาดที่สุด
Lukas Lechner

4
โปรดทราบว่าหากคุณใช้filterIsInstanceและรายการต้นฉบับมีองค์ประกอบของประเภทอื่นรหัสของคุณจะกรองออกโดยไม่โต้ตอบ บางครั้งนี่คือสิ่งที่คุณต้องการ แต่บางครั้งคุณอาจจะIllegalStateExceptionโยนหรือคล้าย ๆ กัน หากเป็นเช่นนั้นในภายหลังคุณสามารถสร้างวิธีการของคุณเองเพื่อตรวจสอบแล้วส่ง:inline fun <reified R> Iterable<*>.mapAsInstance() = map { it.apply { check(this is R) } as R }
mfulton26

3
โปรดทราบว่า.applyไม่ส่งคืนค่าที่ส่งคืนของแลมบ์ดาจะส่งคืนวัตถุที่ได้รับ คุณอาจต้องการใช้.takeIfหากคุณต้องการให้ตัวเลือกส่งคืนค่าว่าง
bj0

10

เพื่อปรับปรุงคำตอบของ @ hotkey นี่คือวิธีแก้ปัญหาของฉัน:

val waypointList = list.filterIsInstance<Waypoint>().takeIf { it.size == list.size }

สิ่งนี้จะช่วยให้คุณList<Waypoint>สามารถร่ายไอเท็มทั้งหมดได้หรือไม่ก็เป็นโมฆะ


3

ในกรณีของคลาสทั่วไปไม่สามารถตรวจสอบได้เนื่องจากข้อมูลประเภทถูกลบในรันไทม์ แต่คุณตรวจสอบว่าวัตถุทั้งหมดในรายการเป็นWaypointของดังนั้นคุณสามารถระงับคำเตือนด้วย@Suppress("UNCHECKED_CAST")ดังนั้นคุณก็สามารถปราบปรามการเตือนด้วย

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


1

ฉันสร้างรูปแบบเล็ก ๆ น้อย ๆ ให้กับคำตอบ @hotkey เมื่อใช้ในการตรวจสอบวัตถุต่อเนื่องกับรายการ:

    @Suppress("UNCHECKED_CAST")
    inline fun <reified T : Any> Serializable.checkSerializableIsListOf() =
        if (this is List<*> && this.all { it is T })
          this as List<T>
        else null

กำลังมองหาวิธีแก้ปัญหาเช่นนี้ แต่เกิดข้อผิดพลาด:Cannot access 'Serializable': it is internal in 'kotlin.io'
daviscodesbugs

0

แทน

myGenericList.filter { it is AbstractRobotTurn } as List<AbstractRobotTurn>

ฉันชอบทำ

myGenericList.filter { it is AbstractRobotTurn }.map { it as AbstractRobotTurn }

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.