I'm trying an example in kotlin, like:
fun test(){
val harfler = listOf("a","b",'c','d')
println(harfler.all {
it.javaClass == String::class.java || it.javaClass == Char::class.java
})
}
List contains Char
or String
but all
function in this expression returns false
,Why return false
?
Can anybody explain it?
Edit
for @JBNizet
As @JB Nizet has already told you how to analyze the problem.
According to the Mapped Types, The Kotlin Char
will be mapped to Java Type decide on its declaration.
- when declare as a non-nullable type
Char
it is a primitive Java type char
.
- when declare as a nullable type
Char?
it is a Java wrapper type Character
.
when declare as a type argument List<Char>
it is a Java wrapper type Character
.
val it = 'a'
// v--- it should be `Any`
val array: Array<Any> = arrayOf('a')
// v--- char
println(it.javaClass)
// v--- print [java.lang.Character]
println(array.map { it.javaClass })
But I want to say that there is a different between the usage and the declaration.
For example, the parameter type it
is a java.lang.Character
, but its javaClass
is char
.
fun typeOf(it: Char?) = it?.javaClass
fun test() {
// v--- java.lang.Character
println(::typeOf.javaMethod!!.parameterTypes[0])
// v--- but it return `char` rather than `java.lang.Character`
println(typeOf('a'))
}
And the example below show the different as further, this is why I declare the array type to Array<Any>
rather than Array<Char>
in preceding example:
// v--- uses `java.lang.Character` instead
val array: Array<Char> = arrayOf('a')
// v--- java.lang.Character
println(array.javaClass.componentType)
// v--- [char]
println(array.map { it.javaClass })
Why did the strange behavior occurs in Koltin?
This is because Kotlin Char
and other wrapper classes represent 2 roles. one is a Java primitive type char
, another is a Java wrapper class java.lang.Character
. However, Kotlin Char
is statically which means you can't change its type in runtime. and a Char
should be mapped to a char
by default in Kotlin.
IF you want get the wrapper type every time, you should use KClass.javaObjectType
instead, for example:
// v--- char
println(Char::class.java)
// v--- java.lang.Character
println(Char::class.javaObjectType)
The Iterable#all
operation is a short-circuiting operation, which means if any first element didn't satisfied will return false
immediately.
inline fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean {
// return `false` immediately the condition didn't satisfied
// v
for (element in this) if (!predicate(element)) return false
return true
}
When checking a Kotlin class like as Char
and others. you should use the Kotlin type checking mechanism rather than traditional comparing approach, it helps you avoid such a confusion. for example:
val anything: Array<Any> = arrayOf('a')
val chars: Array<Char> = arrayOf('a')
println(chars.all { it is Char }) // print true
println(anything.all { it is Char }) // print true
So your code can replace with type checking as below:
fun test() {
val harfler = listOf("a", "b", 'c', 'd')
// v---------------v--- use type checking here
println(harfler.all { it is String || it is Char }) // print true
}