I'am trying to check an amount of specific type in list, by passing the data type to function, but it gets a wrong data type
fun <T> checkTypesOfElements(list: List<Any>, clazz: Class<T>): Unit {
println(clazz.name::class.simpleName) // DataType for check
list.forEach { println(it::class.simpleName) } // Check of DataTypes
println(list.count { it::class == clazz.name::class }) // result
}
fun main() {
val listOfObjects = listOf(51, "Hello", 42, "Kotlin", 123L)
checkTypesOfElements(listOfObjects, Long::class.java)
}
But it outputs this:
String
Int
String
Int
String
Long
2
Why is the first line String
and not Long
?
I'am trying to check an amount of specific type in list, by passing the data type to function, but it gets a wrong data type
fun <T> checkTypesOfElements(list: List<Any>, clazz: Class<T>): Unit {
println(clazz.name::class.simpleName) // DataType for check
list.forEach { println(it::class.simpleName) } // Check of DataTypes
println(list.count { it::class == clazz.name::class }) // result
}
fun main() {
val listOfObjects = listOf(51, "Hello", 42, "Kotlin", 123L)
checkTypesOfElements(listOfObjects, Long::class.java)
}
But it outputs this:
String
Int
String
Int
String
Long
2
Why is the first line String
and not Long
?
2 Answers
Reset to default 3clazz.name
is a String
. Therefore clazz.name::class
is always String::class
, and the simpleName
of String::class
is of course String
.
It seems like you have overcomplicated this. To print the simple name of clazz
, just do
println(clazz.simpleName)
You made a similar mistake in the third line. It should be:
println(list.count { it::class.java == clazz })
This still will not count the number of Long
s correctly, because the Class
argument you passed in main
refers to the class of the primitive Java type (long.class
in Java), whereas the elements in the list are instances of java.lang.Long
(Long.class
in Java). You need to pass Long::class.javaObjectType
instead:
checkTypesOfElements(listOfObjects, Long::class.javaObjectType)
If you use the Kotlin KClass
instead of the Java Class
, there will not be such complications. Here I have rewritten the method to take a KClass
and return an Int
.
fun checkTypesOfElements(list: List<Any>, clazz: KClass<*>) =
list.count { it::class == clazz }
// usage:
println(checkTypesOfElements(listOfObjects, Long::class)) // prints 1
With clazz.name::class.simpleName
you access the name
property of the Class object, which is, well, the name - as a String, like "long"
. You then retrieve the class of the string "long"
which is String
.
What you really want to do is use the class itself:
println(clazz.simpleName)
This also applies to the actual comparison:
println(list.count { it::class == clazz })
Although this solves the issue with the Long being mistaken as a String, your comparison still won't work. The reason for that becomes more clear if you do not use simpleName
. If you simply remove it, you get this output now:
long
class kotlin.Int
class kotlin.String
class kotlin.Int
class kotlin.String
class kotlin.Long
0
The class passed as a parameter seems to be somehow different than the class retrieved from the actual objects. In fact, the parameter is of type java.lang.Class
where the class you retrieve from the objects is of type kotlin.reflect.KClass
. After all, the parameter you pass to checkTypesOfElements is Long::class.java
, a Kotlin class converted to a Java class.
To fix the mismatch you could also convert the object's class to a Java class, like this:
list.forEach { println(it::class.java) } // Check of DataTypes
println(list.count { it::class.java == clazz }) // result
Although this will work with most objects now (like String
), it actually won't work for Long
. From the output you can see that the parameter you pass is long
, where the type of the actual object is class java.lang.Long
:
long
class java.lang.Integer
class java.lang.String
class java.lang.Integer
class java.lang.String
class java.lang.Long
0
The reason is that the Java type system differentiates between the primitive type long
and the object type Long
. To circumvent this, you can switch away from Java types and move to Kotlin types, like this:
fun <T: Any> checkTypesOfElements(list: List<Any>, clazz: KClass<T>): Unit {
println(clazz) // DataType for check
list.forEach { println(it::class) } // Check of DataTypes
println(list.count { it::class == clazz }) // result
}
fun main() {
val listOfObjects = listOf(51, "Hello", 42, "Kotlin", 123L)
checkTypesOfElements(listOfObjects, Long::class)
}
And this finally reproduces the expected output:
class kotlin.Long
class kotlin.Int
class kotlin.String
class kotlin.Int
class kotlin.String
class kotlin.Long
1
Class.getName()
, which will always be string. Why don't you just useprintln(clazz.simpleName)
? – Jon Skeet Commented Mar 17 at 10:44