dave08
01/29/2023, 3:46 PMprivate val actions = mutableMapOf<String, SomeInterface>()
fun <T : SomeInterface> getOrNull(key: String): T? =
actions[key] as? T
val tryToGetAFoo = getOrNull<Bar>("something")
// I have a Foo in the "something" key so this should be null... Bar and Foo are both implementation of SomeInterface...
what is this error that I'm getting: class Bar cannot be cast to class Foo (Bar and Foo are in unnamed module of loader 'app') ?Chris Lee
01/29/2023, 3:48 PMFoo is not a Bar even though they implement the same interface. The as? T combined with getOrNull<Bar> casts everything to a Bar.dave08
01/29/2023, 3:49 PMas? operator returns null if it doesn't succeed in casting...Joffrey
01/29/2023, 3:52 PMJoffrey
01/29/2023, 3:52 PMdave08
01/29/2023, 3:53 PMclass Bar cannot be cast to class Foo (Bar and Foo are in unnamed module of loader 'app')Chris Lee
01/29/2023, 3:53 PMChris Lee
01/29/2023, 3:53 PMdave08
01/29/2023, 3:53 PMJoffrey
01/29/2023, 3:57 PMUnchecked cast: SomeInterface? to T. This means that even though you're writing an explicit cast here with as?, it will not check the type of the value at runtime, so the cast will never fail and the value will always be returned as-is. That is why the function happily returns a Foo even when called with <Bar>.
The cast that fails is probably not the one you wrote with as?, but the one the compiler does automatically afterwards during the variable assignment.
To fix it, you can use an inline function with reified T, so the cast is not unchecked:
private inline fun <reified T : SomeInterface> getOrNull(key: String): T? =
actions[key] as? T
https://pl.kotl.in/LgsspJQUHChris Lee
01/29/2023, 3:58 PMinline fun <reified T : SomeInterface> getOrNull(key: String): T? =
actions[key] as? Tdave08
01/29/2023, 3:59 PMactions is private in the class... so an inline function won't work here...Joffrey
01/29/2023, 3:59 PM@PublishedApi internalChris Lee
01/29/2023, 4:00 PMdave08
01/29/2023, 4:00 PMor have a helper functionhow? I can't cast to a KClass...?
Chris Lee
01/29/2023, 4:01 PMdave08
01/29/2023, 4:01 PMThen make itIt's used in the same module..@PublishedApi internal
dave08
01/29/2023, 4:03 PMJoffrey
01/29/2023, 4:03 PMactions visible to the module, you can either provide an untyped helper as suggested by Chris, or make a typed helper that uses a KClass, and then a reified helper for convenience:
inline fun <reified T : SomeInterface> getOrNull(key: String): T? = getOrNull(key, T::class)
fun <T : SomeInterface> getOrNull(key: String, type: KClass<T>): T? =
type.safeCast(actions[key])dave08
01/29/2023, 4:06 PMtype.safeCast require the reflection libs?Joffrey
01/29/2023, 4:07 PMkotlin.reflect.full so there is a chance, yesJoffrey
01/29/2023, 4:07 PMactions as a read-only map, to be frank 😄dave08
01/29/2023, 4:09 PMChris Lee
01/29/2023, 4:10 PMgetOrNull to be an extension function for a more natural feel.
val actions = mutableMapOf<String, SomeInterface>()
interface SomeInterface {
}
class Foo : SomeInterface {
}
class Bar: SomeInterface {
}
inline fun <reified T : SomeInterface> Map<String,SomeInterface>.getOrNull(key: String): T? =
actions[key] as? T
//public fun getAction(key : String) : SomeInterface? {
// return actions[key]
//}
// I have a Foo in the "something" key so this should be null... Bar and Foo are both implementation of SomeInterface...
/**
* You can edit, run, and share this code.
* <http://play.kotlinlang.org|play.kotlinlang.org>
*/
fun main() {
actions["foo"] = Foo()
actions["bar"] = Bar()
val tryToGetAFoo = actions.getOrNull<Bar>("foo")
}Chris Lee
01/29/2023, 4:12 PMTrue in general, but here I’m abstracting away the fact that the key is a value class internally.Is it really abstracted away, though? Having to pass in a key to getOrNull exposes that there’s a key/value relationship.
dave08
01/29/2023, 4:14 PMJoffrey
01/29/2023, 4:28 PMJoffrey
01/29/2023, 4:28 PMdave08
01/29/2023, 4:31 PMJoffrey
01/29/2023, 4:32 PM