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 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 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? T
dave08
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 internal
Chris 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
Joffrey
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, yesactions
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")
}
True 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 PMdave08
01/29/2023, 4:31 PMJoffrey
01/29/2023, 4:32 PM