Here's the answers to the
chapter 10 exercise.
Exercise: Basic Dependency Injection
So here's the implementation of the build method:
val objMap = mutableMapOf<KClass<*>, Any>()
inline fun <reified T: Any> build() =
build(T::class)
@Suppress("UNCHECKED_CAST")
fun <T: Any> build(klass: KClass<T>): T {
if (objMap.containsKey(klass)) {
return objMap[klass] as T
} else {
with(klass.constructors.first()) {
val paramObjs = parameters
.map { build(it.type.classifier as KClass<*>) }
.toTypedArray()
val obj = call(*paramObjs)
objMap[klass] = obj
return obj
}
}
}
For the bonus exercise of adding a @Singleton annotation, we'd want to define the annotation like this:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Singleton
And then rework the build method like this:
@Suppress("UNCHECKED_CAST")
fun <T: Any> build(klass: KClass<T>): T {
if (objMap.containsKey(klass)) {
return objMap[klass] as T
} else {
with(klass.constructors.first()) {
val paramObjs = parameters
.map { build(it.type.classifier as KClass<*>) }
.toTypedArray()
val obj = call(*paramObjs)
if (klass.findAnnotation<Singleton>() != null) {
objMap[klass] = obj
}
return obj
}
}
}
With this code in place, if you run our example with Nodes 1 through 4, you'll notice that the Node1 ids are different when it doesn't have the @Singleton annotation, but they are the same if you annotate the Node1 class with @Singleton.