I have a list for example of type People
. My list can contain only elements of type Student
or only elements of type 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
At some point I need to know the exact type of the list (student or worker). Can I safely find out the exact type? So far I've written this code, but it doesn't look very good:
fun someLogic(items: List<People>): List<People> {
return (items as? List<Student>) ?: (items as? List<Worker>)
?.filter {}
....
}
Also, I get a warning:
Unchecked cast
Can you please tell me how to perform such transformations correctly?
CodePudding user response:
If you want to check that List<People>
is List<Student>
you can use this extension function:
fun List<People>.isStudentList(): Boolean {
// returns true if no element is not Student, so all elements are Student
return none { it !is Student }
}
And if you want to cast List<People>
to List<Student>
, you can use map, and this cast is safe so let's say that there is some People
that the are not Student
so the cast is going to return null instead of Student
because of as?
and the mapNotNull
is going to exclude null elements so in worst cases where you pass a list that doesn't contain any Student
this function is going to return an empty list:
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 }
}
And the same approach can be used for Worker
CodePudding user response:
I would solve the problem with more specific classes.
You can define:
interface PeopleList<P : People> : List<P>
class StudentList : PeopleList<Student> {
// add implementation
}
class WorkerList : PeopleList<Worker> {
// add implementation
}
You can then easily check the types of these lists. Each of those classes can then provide guarantees that you are not mixing Student
and Worker
objects in the same List
, something you can't do with plain List<People>
objects.
Note also you are better off writing your code avoiding checking types if at all possible. Much better to add methods to the PeopleList
interface and force the subclasses to implement them, for example:
interface PeopleList<P : People> : List<P> {
fun doSomethingGood()
}
Then you can call these methods at the appropriate time, instead of checking the type. This approach keeps the functionality associated with the subtypes alongside those subtypes and not scattered through the code at the various points where you have to check the type of PeopleList
.