I want to create a hierarchy of classes (a parent ...
# getting-started
f
I want to create a hierarchy of classes (a parent / child relationship) where each child has a refernce to its parent. I want to use
reified
so that I get the type of the parent instead of a generic type, but I get an error that reads
Copy code
Cannot use "CapturedType(out Root)" as reified type parameter
I have the sample code in the thread. Any suggestions how to go about this?
1
Copy code
object Bindings {
    val map = mutableMapOf<KClass<out Scope>, Scope>()

    inline fun <reified T : Scope> register(value: T) {
        map[T::class] = value
    }

    inline fun <reified T : Scope> get(key: KClass<T>): T {
        return map[key] as T
    }
}

interface Scope {
    val parent: Scope?
}

object Root : Scope {
    override val parent: Scope? = null

    fun something() {
        println("Something called")
    }
}

object Child : Scope {
    override val parent: Root = Root
}

fun main() {
    Bindings.register(Root)
    Bindings.register(Child)
    val parent = Bindings.get(key = Child.parent::class) <-- error here
    parent.something()
}
y
Your
get
function doesn't need to be reified. I believe you can just remove the reified there and the code should compile
I don't think I really understand what the purpose of the code is though. Both
Child.parent
and
Bindings.get(Child.parent::class)
will have the same value and the same type in the type system
f
without the
reified
you can't cast to T and you get a generic scope. This is a contrived example, but I would like to understand how to get it to work
y
Without it, you can still cast to T, and the real cast happens at the call site. Basically,
get
needs no knowledge of the reified type of T, only of its class. The caller to
get
, when they use some reified type parameter T, will then cause the return value to be casted to T, thus the compiler will add a check in the bytecode to ensure that the type of the value is correct
f
yes, but I don't want to cast at the call site, the intention is to have it return the actual type
y
Lemme make myself clearer. The only difference that having
get
be reified makes is that, at runtime, it does a check on the value that was gotten from the Map. That check can be done without reification by using
KClass.isInstance
. Thats the only difference. During compile time, it doesn't matter that
get
is or isn't reified when it comes to the returned type. It will always, always, always give you the same compile-time-inferred type of the KClass you passed in. The reification of
get
doesn't affect that. The error you're getting, btw, is because when you do
parent::class
, you get back a
KClass<out Blah>
instead of a
KClass<Blah>
, and so Kotlin complains because it doesn't want to use
out Blah
as a reified type. I think that replacing the signature of
get
to have
klass: KClass<out T>
might allow you to keep it reified, but this still doesn't change the fact that it's much simpler and entirely equivalent to make
get
not reified and simply just have it perform an
isInstance
check inside its body.
f
thanks, this works, as you suggested
Copy code
inline fun <reified T : Scope> get(key: KClass<out T>): T
I'm not clear on the
isInstance
part, could you share a snippet of how that'd work?
y
fun <T : Scope> get(key: KClass<out T>): T = (map[key] as T).also { require(key.isInstance(it) }
f
thanks, that works too, though this one has the cast warning
w
It looks like you could also use
KClass::cast
Copy code
fun <T : Scope> get(key: KClass<out T>): T = key.cast(map[key])