title: Kotlin custom simple DI - how to know the type without explicit declaration before calling instantination function?
body: <p>I'm currently learning Kotlin and making different stuff to face problems on the go.</p>
  <p>Now I have simple DI implementation for sample project - which works - but I have questions regarding implementation because usage differs from Koin (I suppose they did it better).</p>
  <p>In my DI I provide module as List where Item is an interface which is implemented by Single and Factory classes. It requires KClass and factory method for constructor. This makes module declaration look like this:</p>
  <pre><code>val module: List&lt;Item&gt; = listOf(
    Factory(DatabaseKernel::class) { KernelNative() }
  ) 
  </code></pre>
  <p>And Koin module looks like this:</p>
  <pre><code>private val model = module()
  {
    factory&lt;Gson&gt; { Gson() }
  }
  </code></pre>
  <p>I understand that in Koin's case factory() is an inline function. However, I don't understand the choice to use function instead of object. I guess it could be for an additional functionality that I don't need.</p>
  <p>The main point is that I don't actually have to specify type for above example and could just provide <code>factory { Gson() }</code>. In my implementation I don't know the type of object unless I call construction function, which is unwanted since it would just create an instance for every Item in module. But I need to know type for lookup.</p>
  <p>What am I missing in part of reading type in Koin? Does it actually instantiate every object in module? Cause I'd like to use lazy whenever possible.</p>
  <p>Another question would be too broad, but answers are welcome - what am I missing apart from that issue I have? Maybe in implementation best practices. Now I think that I could probably use Map&lt;KClass, Any&gt; instead of list, but I'm unsure whether it will give any benefit in case of complexity for find()/get(), cause now it is O(N) for list and I don't know if hashCode() for mine KClass is unique.</p>
  <p>Also now I'm realized that Map implementation washes away any hopes of not specifying type.</p>
  <p>Example usage:</p>
  <pre><code>val foo: DatabaseKernel by inject()
  </code></pre>
  <p>Bellow is a full implementation as list:</p>
  <pre><code>internal object Inject
  {
    val module: List&lt;Item&gt; = listOf(
      Factory(DatabaseKernel::class) { KernelNative() }
    )
  
    inline fun &lt;reified T: Any&gt; inject(): Lazy&lt;T&gt; =
      lazy { module.find { it.type == T::class }?.get() as? T ?: error(&quot;Class ${T::class} not specified&quot;) }
  
    interface Item
    {
      val type:      KClass&lt;out Any&gt;
      val construct: () -&gt; Any
  
      fun get(): Any
    }
  
    private data class Single(override val type: KClass&lt;out Any&gt;, override val construct: () -&gt; Any) : Item
    {
      private val instance: Any by lazy(construct)
  
      override fun get(): Any = instance
    }
  
    private data class Factory(override val type: KClass&lt;out Any&gt;, override val construct: () -&gt; Any) : Item
    {
      override fun get(): Any = construct()
    }
  }
  </code></pre>
  <p>And here is implementation with Map:</p>
  <pre><code>internal object FMPInject
  {
    val module: Map&lt;KClass&lt;out Any&gt;, Item&gt; = mapOf(
      DatabaseKernel::class to Factory { KernelNative() }
    )
  
    inline fun &lt;reified T: Any&gt; inject(): Lazy&lt;T&gt; =
      lazy { module[T::class]?.get() as? T ?: error(&quot;Class ${T::class} not specified&quot;) }
  
    interface Item
    {
      val construct: () -&gt; Any
  
      fun get(): Any
    }
  
    private data class Single(override val construct: () -&gt; Any) : Item
    {
      private val instance: Any by lazy(construct)
  
      override fun get(): Any = instance
    }
  
    private data class Factory(override val construct: () -&gt; Any) : Item
    {
      override fun get(): Any = construct()
    }
  }
  </code></pre>
  <p>Solution:</p>
  <p>As @Joffrey pointed out, I could use reified factory() and single() functions that infer type without explicitly specifying it. Bellow is complete example how it works (with list).</p>
  <pre><code>internal object FMPInject
  {
    val module: List&lt;Item&gt; = listOf(
      factory&lt;DatabaseKernel&gt; { KernelNative }, // explicit interface type
      factory { Gson() } // infers from constructor function
    )
  
    inline fun &lt;reified T: Any&gt; inject(): Lazy&lt;T&gt; =
      lazy { module.find { it.type == T::class }?.get() as? T ?: error(&quot;Class ${T::class} not specified&quot;) }
  
    private inline fun &lt;reified T: Any&gt; factory(noinline constructor: () -&gt; T) = Factory(T::class, constructor)
  
    private inline fun &lt;reified T: Any&gt; single(noinline constructor: () -&gt; T) = Single(T::class, constructor)
  
    interface Item
    {
      val type:      KClass&lt;out Any&gt;
      val construct: () -&gt; Any
  
      fun get(): Any
    }
  
    private data class Single(override val type: KClass&lt;out Any&gt;, override val construct: () -&gt; Any) : Item
    {
      private val instance: Any by lazy(construct)
  
      override fun get(): Any = instance
    }
  
    private data class Factory(override val type: KClass&lt;out Any&gt;, override val construct: () -&gt; Any) : Item
    {
      override fun get(): Any = construct()
    }
  }
  </code></pre>
  <p>I also found out that Koin uses Set for storage, which provides unique/override functionality.</p>