如何在Kotlin中正确地铸造列表?

lf3rwulv  于 2022-11-30  发布在  Kotlin
关注(0)|答案(3)|浏览(201)

例如,我有一个People类型的列表。我的列表只能包含Student类型的元素或Worker类型的元素:

interface People {
        val name: String
        val age: Int
    }

    data class Student(
        override val name: String,
        override val age: Int,
        val course: Int
    ) : People

    data class Worker(
        override val name: String,
        override val age: Int,
        val position: String
    ) : People

在某些时候,我需要知道列表的确切类型(student或worker)。我能安全地找到确切类型吗?到目前为止,我已经写了下面的代码,但看起来不是很好:

fun someLogic(items: List<People>): List<People> {
    return (items as? List<Student>) ?: (items as? List<Worker>)
?.filter {}
....
}

同时,我收到警告:
未检查的转换
你能告诉我如何正确地执行这样的转换吗?

w46czmvw

w46czmvw1#

在运行时,用于创建列表的类型参数不可用。例如,无法区分以下两种情况:

val students: List<People> = listOf<Student>(student1, student2)
val people: List<People> = listOf<People>(student1, student2)

这是因为类型擦除。
在运行时,可以帮助确定列表元素类型的唯一信息是其元素的类型。
因此,如果一个列表没有元素,就没有办法知道它是什么类型的列表,尽管在大多数情况下,你根本不需要知道。
因此,假设列表只能是所有学生的列表或所有工人的列表,而不能是包含学生和工人的混合列表,则可以通过检查第一个元素来确定列表的类型。

when (items.firstOrNull()) {
    null -> { /* cannot determine the type */ }
    is Student -> { /* is a list of students */ }
    is Worker -> { /* is a list of worker */ }

    // you can remove this branch by making the interface sealed
    else -> { /* someone made another class implementing People! */  }
}

另一方面,如果你想得到List<Student>List<Worker>out,你可以使用filterIsInstance

val students = items.filterIsInstance<Student>()
val worker = items.filterIsInstance<Worker>()

无论哪个列表不为空,则items的类型是该列表的类型。

5f0d552i

5f0d552i2#

如果您想检查List<People>是否为List<Student>,可以使用以下扩展函数:

fun List<People>.isStudentList(): Boolean {
    // returns true if no element is not Student, so all elements are Student
    return all { it is Student } 
}

如果您想将List<People>转换为List<Student>,您可以使用map,这个类型转换是安全的,所以假设有一些People不是Student,所以这个类型转换会返回null而不是Student,因为as?mapNotNull会排除null元素,所以在最坏的情况下,当你传递一个不包含t包含任何Student这个函数将返回一个空列表:

fun List<People>.toStudentList(): List<Student> {
    // This is going to loop through the list and cast each People to Student
    return mapNotNull { it as? Student }
}

或者您可以只使用filterIsInstance<Student>,其工作方式与上面的toStudentList相同:

list.filterIsInstance<Student>()

同样的方法也可以用于Worker

axzmvihb

axzmvihb3#

我会用更具体的类来解决这个问题。
您可以定义:

interface PeopleList<P : People> : List<P>

class StudentList : PeopleList<Student> {
   // add implementation
}

class WorkerList : PeopleList<Worker> {
   // add implementation
}

然后你可以很容易地检查这些列表的类型,每个类都可以保证你没有在同一个List中混合StudentWorker对象,这是你不能用普通的List<People>对象做的。
还要注意,如果可能的话,你最好在编写代码时避免检查类型。最好在PeopleList接口中添加方法,并强制子类实现它们,例如:

interface PeopleList<P : People> : List<P> {
    fun doSomethingGood()
}

然后,您可以在适当的时候调用这些方法,而不是检查类型。这种方法将与这些子类型关联的功能与这些子类型保持在一起,而不是分散在代码中您必须检查PeopleList类型的各个点。

相关问题