Iterate enum values using values() and valueOf in kotlin - kotlin

Am a newbie here. Can anyone give an example to iterate an enum with values and valueOf methods??
This is my enum class
enum class Gender {
Female,
Male
}
I know we can get the value like this
Gender.Female
But I want to iterate and display all the values of Gender. How can we achieve this? Anyhelp could be appreciated

You can use values like so:
val genders = Gender.values()
Since Kotlin 1.1 there are also helper methods available:
val genders = enumValues<Gender>()
With the above you can easily iterate over all values:
enumValues<Gender>().forEach { println(it.name) }
To map enum name to enum value use valueOf/enumValueOf like so:
val male = Gender.valueOf("Male")
val female = enumValueOf<Gender>("Female")

You're getting [LGender;#2f0e140b or similar as the output of printing Gender.values() because you're printing the array reference itself, and arrays don't have a nice default toString implementation like lists do.
The easiest way to print all values is to iterate over that array, like this:
Gender.values().forEach { println(it) }
Or if you like method references:
Gender.values().forEach(::println)
You could also use joinToString from the standard library to display all values in a single, formatted string (it even has options for prefix, postfix, separator, etc):
println(Gender.values().joinToString()) // Female, Male

You can add this method to your enum class.
fun getList(): List<String> {
return values().map {
it.toString()
}
}
And call
val genders = Gender.getList()
// genders is now a List of string
// Female, Male

You can do this and get a new array of your enum type Gender
val arr = enumValues<Gender>()
and if you want a list of its, you can use the extension. toList()

Related

How to correctly cast list in Kotlin?

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?
At runtime, the type parameter you used to create the list is not available. e.g. it is impossible to distinguish between the following two situations:
val students: List<People> = listOf<Student>(student1, student2)
val people: List<People> = listOf<People>(student1, student2)
This is because of type erasure.
The only information you have at runtime that can help determine a list's element type is the type of its elements.
So if a list has no elements, there is no way of knowing what type of list it is. Though in most situations, you don't need to anyway.
So assuming the list can only be a list of all students, or a list of all workers, but not a list containing a mixture of students and workers, you can determine the type of the list by checking the first element.
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! */ }
}
If you want to get a List<Student> or List<Worker> out of this on the other hand, you can just use filterIsInstance:
val students = items.filterIsInstance<Student>()
val worker = items.filterIsInstance<Worker>()
whichever list is not empty, then the type of items is the type of that list.
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 all { 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 }
}
Or you can just use filterIsInstance<Student> this will work the same as toStudentList above:
list.filterIsInstance<Student>()
And the same approach can be used for Worker
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.

How to count the number of properties in a complex class

I have a class with several properties. The properties, themselves, can also have their own properties. I would like to count the total number of properties. That includes both the properties in the "main" class and the properties' properties.
For instance, consider the following class
class Person {
val firstname: String = "Jurgen"
val lastname: String = "Klopp"
val address: Address = Address("Liverpool", "England")
}
where
class Address (
val city: String,
val coubtry: String
) { }
I would like the counting to add up to 5, since the Person class "contains" firstname, lastname, address, city and country. Note that the Address class also could have a another class (with its own properties) as its property. These properties should also be counted. Is it possible to count the total number of properties?
Please further note that the counting is intended to be applied to complex/multi-leveled AVRO structures (i.e. auto-generated AVRO classes).
I'm basing this off my other post on reflection, found here: How to get the relative class name of data classes
I created your two classes in a package called complexclasses.
My Main.kt looks like this:
import complexclasses.Person
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
fun main(args: Array<String>) {
val foundParams = Person::class.getAllMembers()
println(foundParams)
}
private fun KClass<*>.getAllMembers(): MutableList<String> {
val params = mutableListOf<String>()
memberProperties.forEach { member ->
params.add(member.name)
if(member.returnType.toString().substring(0, 7) == "kotlin.") {
return#forEach
}
val clazz = Class.forName(member.returnType.toString()).kotlin
params.addAll(clazz.getAllMembers())
}
return params
}
When I run the program, it outputs:
[address, city, country, firstname, lastname]
Using reflection (and a little string manipulation) I can recursively crawl non-kotlin objects and print out the names of all their members.
I didn't test this with more complicated data structures, like Lists or Maps. It's possible those would require more work, but I think this is enough to get you started, at least.
I'm not familiar with AVRO so hopefully this works with it.
Let me know if you hit any issues, I'm glad to help if this doesn't meet your requirements.

How to effectively map between Enum in Kotlin

I have two Enums,
enum class EnumKey
enum class EnumValue
and I already have a mapping from EnumKey to EnumValue.
fun EnumKey.toEnumValue(): EnumValue =
when(this) {
EnumA.KEY1 -> EnumValue.VALUE1
EnumA.KEY2 -> EnumValue.VALUE2
...
...
EnumA.KEY1000 -> EnumValue.VALUE1000
}
Now I need to have an another mapping from EnumValue to EnumKey.
Is using a Map and its reversed map created by associateBy the best way to do it? Or is there any other better ways?
Thanks!
If the enum values are somehow connected by name and they're as large as in your example, then I would advise using something like EnumValue.values().filter { it.name.contains(...) } or using regex.
If they aren't and the connection needs to be stated explicitly then I would use an object (so it's a singleton like the enums themselves) and have this mapping hidden there:
object EnumsMapping {
private val mapping = mapOf(
EnumKey.A to EnumValue.X,
EnumKey.B to EnumValue.Y,
EnumKey.C to EnumValue.Z,
)
....
and next, have the associated values available by functions in this object like:
fun getEnumValue(enumKey: EnumKey) = mapping[enumKey]
and
fun getEnumKey(enumValue: EnumValue) = mapping.filterValues { it == enumValue }.keys.single()
If it's often used or the enums are huge, and you're troubled by the performance of filtering the values every time, then you can create the association in the second way, just like you've proposed:
private val mapping2 = mapping.toList()
.associate { it.second to it.first }
and then have the second function just access this new mapping.
Writing the extension functions like you've provided, but using this object, will result in cleaner code and having the raw association still in one place.

How can I create a List (or any other collection) of another class property's type dynamically?

This feature is inspired by TypeScript which allows us to create arrays based on the property of another class, whatever that property's type is.
For example assume you have this class in Kotlin:
class Person(
val name: String,
val age: Int
)
And later, somewhere else in the code I want to have a list of names, so I would do something like this:
val namesList = List<Person::name>()
And Kotlin will know that this will be equivalent to List<String>() at compile time.
This avoids me to manually propagate the type of a field I already declared in one place. Plus, if one day the name type changes from String to something else, all the collections would get updated automatically.
Can this be done in Kotlin?
No, Kotlin is very explicit about types. It is a strongly-typed language.
Maybe the closest you could do is define a type alias next to your class and use that:
typealias PersonName = String
data class Person(val name: PersonName, val age: Int)
and then:
val namesList = mutableListOf<PersonName>()
However, in most cases you don't have to explicitly write the types anyway because they can be inferred.
// Is a List<String> and would automatically update if name type changed
val nameList = personList.map(Person::name)
// Or to get an empty mutable list:
val nameList = emptyList<Person>().map(Person::name).toMutableList()
The standard thing to do is to use map to extract the type you need:
val people = listOf(
Person("a", 1),
Person("b", 2),
Person("c", 3),
)
val names = people.map { it.name } // statically inferred to List<String>
If you changed the type of name to something else, you wouldn't need to change the val names = people.map { it.name } line - the new type will be inferred automatically.

Check type of ArrayList in Kotlin

Kotlin provides Array.isArrayOf() for checking if an array is of a certain type.
It's used like this
if(object.isArrayOf<String>())
And defined like this
/**
* Checks if array can contain element of type [T].
*/
#Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
public fun <reified T : Any> Array<*>.isArrayOf(): Boolean =
T::class.java.isAssignableFrom(this::class.java.componentType)
But it's only for Array. I need to check ArrayList.
I thought to change the signature like so.
#Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
public fun <reified T : Any> ArrayList<*>.isArrayListOf(): Boolean =
T::class.java.isAssignableFrom(this::class.java.componentType)
but class.java.componentType is specific to Array
How can I check what type of ArrayList I have?
I should clarify, I only care if its one of 3 types, so I don't need a completely open-ended way of checking.
If you want to check the type of a list you can do:
when (list.firstOrNull()) {
is String -> { /*do something*/ }
is Int -> { /*do another thing*/ }
else -> { /*do something else*/ }
}
And if you need to use the list of a certain type you can use:
list.filterInstance</*the type you need*/>()
Hope this works for you.
You can't. Arrays are the only generic type for which this is possible (because they aren't really generic in the same sense, Kotlin just hides it).
The only thing you can do is look at its contents, but of course
that won't work for empty lists;
if a list contains e.g. a String, it could be ArrayList<String>, ArrayList<CharSequence>, ArrayList<Any>, etc.
For this purpose:
I need to direct it into the appropriate Bundle method. bundle.putStringArrayList(), bundle.putIntegerArrayList(), ect
neither should be a problem, I believe.
If the list is of one type then you can convert the list to array using: toTypedArray() and after you can then check the type using: isArrayOf
But this would be inefficient since you are converting the list to array, better if you can just directly guess or retrieved the first item of the list.