Kotlin custom simple DI-在调用Instantiation函数之前,如何知道没有显式声明的类型?

Kotlin custom simple DI-在调用Instantiation函数之前,如何知道没有显式声明的类型?,kotlin,dependency-injection,Kotlin,Dependency Injection,我目前正在学习Kotlin,并制作不同的东西来面对移动中的问题 现在我有了样例项目的简单DI实现——这很有效——但我对实现有疑问,因为使用不同于Koin(我想他们做得更好) 在我的DI中,我以列表的形式提供模块,其中项是由单个类和工厂类实现的接口。它要求构造函数使用KClass和factory方法。这使得模块声明如下所示: val module: List<Item> = listOf( Factory(DatabaseKernel::class) { KernelNative(

我目前正在学习Kotlin,并制作不同的东西来面对移动中的问题

现在我有了样例项目的简单DI实现——这很有效——但我对实现有疑问,因为使用不同于Koin(我想他们做得更好)

在我的DI中,我以列表的形式提供模块,其中项是由单个类和工厂类实现的接口。它要求构造函数使用KClass和factory方法。这使得模块声明如下所示:

val module: List<Item> = listOf(
  Factory(DatabaseKernel::class) { KernelNative() }
) 
private val model = module()
{
  factory<Gson> { Gson() }
}
下面是一个完整的实现,如列表所示:

internal object Inject
{
  val module: List<Item> = listOf(
    Factory(DatabaseKernel::class) { KernelNative() }
  )

  inline fun <reified T: Any> inject(): Lazy<T> =
    lazy { module.find { it.type == T::class }?.get() as? T ?: error("Class ${T::class} not specified") }

  interface Item
  {
    val type:      KClass<out Any>
    val construct: () -> Any

    fun get(): Any
  }

  private data class Single(override val type: KClass<out Any>, override val construct: () -> Any) : Item
  {
    private val instance: Any by lazy(construct)

    override fun get(): Any = instance
  }

  private data class Factory(override val type: KClass<out Any>, override val construct: () -> Any) : Item
  {
    override fun get(): Any = construct()
  }
}
内部对象注入
{
val模块:List=listOf(
工厂(DatabaseKernel::class){KernelNative()}
)
inline fun inject():Lazy=
lazy{module.find{it.type==T::class}?.get()作为?T?:错误(“未指定class${T::class}”)}
接口项
{
val类型:KClass
val构造:()->任意
fun get():任何
}
私有数据类单个(覆盖val类型:KClass,覆盖val构造:()->Any):项
{
私有val实例:任何延迟(构造)
重写fun get():Any=实例
}
私有数据类工厂(覆盖val类型:KClass,覆盖val构造:()->Any):项
{
重写fun get():Any=construct()
}
}
下面是Map的实现:

internal object FMPInject
{
  val module: Map<KClass<out Any>, Item> = mapOf(
    DatabaseKernel::class to Factory { KernelNative() }
  )

  inline fun <reified T: Any> inject(): Lazy<T> =
    lazy { module[T::class]?.get() as? T ?: error("Class ${T::class} not specified") }

  interface Item
  {
    val construct: () -> Any

    fun get(): Any
  }

  private data class Single(override val construct: () -> Any) : Item
  {
    private val instance: Any by lazy(construct)

    override fun get(): Any = instance
  }

  private data class Factory(override val construct: () -> Any) : Item
  {
    override fun get(): Any = construct()
  }
}
内部对象FMPInject
{
val模块:Map=mapOf(
DatabaseKernel::类到工厂{KernelNative()}
)
inline fun inject():Lazy=
lazy{module[T::class]?.get()作为?T?:错误(“未指定class${T::class}”)}
接口项
{
val构造:()->任意
fun get():任何
}
私有数据类单个(覆盖val构造:()->任意):项
{
私有val实例:任何延迟(构造)
重写fun get():Any=实例
}
私有数据类工厂(重写val构造:()->Any):项
{
重写fun get():Any=construct()
}
}
解决方案:

正如@Joffrey指出的,我可以使用具体化的factory()和single()函数来推断类型,而无需显式指定类型。下面是它如何工作的完整示例(带列表)

内部对象FMPInject
{
val模块:List=listOf(
工厂{KernelNative},//显式接口类型
工厂{Gson()}//从构造函数推断
)
inline fun inject():Lazy=
lazy{module.find{it.type==T::class}?.get()作为?T?:错误(“未指定class${T::class}”)}
私有内联函数工厂(noinline构造函数:()->T)=工厂(T::类,构造函数)
私有内联fun single(noinline构造函数:()->T)=single(T::class,构造函数)
接口项
{
val类型:KClass
val构造:()->任意
fun get():任何
}
私有数据类单个(覆盖val类型:KClass,覆盖val构造:()->Any):项
{
私有val实例:任何延迟(构造)
重写fun get():Any=实例
}
私有数据类工厂(覆盖val类型:KClass,覆盖val构造:()->Any):项
{
重写fun get():Any=construct()
}
}
我还发现Koin使用Set进行存储,它提供了独特的/覆盖功能

我知道在Koin的例子中,factory()是一个内联函数。然而,我不理解用函数代替对象的选择。我想可能是因为我不需要额外的功能

正如您所指出的,
工厂
函数是一个
内联
函数,它允许使用
具体化的
类型参数,这正是您不需要显式传递
KClass
的原因(函数可以从具体化的类型参数中获取它)

在我的实现中,除非调用构造函数,否则我不知道对象的类型,这是不需要的,因为它只会为模块中的每个项创建一个实例。但我需要知道查找的类型

您不需要计算表达式(在本例中为构造函数)来了解其类型。如果将泛型函数与类型参数
T
一起使用,则可以从作为参数传递的表达式中推断
T

结合上述两条语句,您可以声明以下内容:

inlinefun工厂(构造函数:()->T)=工厂(T::类,构造函数)

请注意,这只是关于DI的外部API,您可以(也可能会)在实现中依赖内部的
KClass

IDE告诉我为函数参数添加noinline。这是正确的行为吗?@NapoleotheCake这是因为我直接将
构造函数
作为变量传递(没有像
{constructor()}
那样封装在lambda中),因此函数本身不能真正内联,因此向其添加
noinline
是有意义的。我们将此函数标记为内联的原因是为了类型参数的具体化,而不是用于具有构造函数的lambda。非常感谢你的这一次。稍后我将更新这个问题。现在我意识到,映射实现消除了任何不指定类型的希望。-你为什么这么说?为什么地图版本在这方面会有什么不同?@Joffrey因为使用地图我必须为地图键指定类。我仍然不明白这个问题。列表版本还使用
项中的类。它与在公共接口中指定类型有什么关系?@Joffrey,实际上什么都没有。这是另一个可能的实现,我必须显式地指定类型。通常的方法是提供公共方法,这些方法确实要求显式的
KClass
,然后还提供具有具体类型的内联方法。
internal object FMPInject
{
  val module: Map<KClass<out Any>, Item> = mapOf(
    DatabaseKernel::class to Factory { KernelNative() }
  )

  inline fun <reified T: Any> inject(): Lazy<T> =
    lazy { module[T::class]?.get() as? T ?: error("Class ${T::class} not specified") }

  interface Item
  {
    val construct: () -> Any

    fun get(): Any
  }

  private data class Single(override val construct: () -> Any) : Item
  {
    private val instance: Any by lazy(construct)

    override fun get(): Any = instance
  }

  private data class Factory(override val construct: () -> Any) : Item
  {
    override fun get(): Any = construct()
  }
}
internal object FMPInject
{
  val module: List<Item> = listOf(
    factory<DatabaseKernel> { KernelNative }, // explicit interface type
    factory { Gson() } // infers from constructor function
  )

  inline fun <reified T: Any> inject(): Lazy<T> =
    lazy { module.find { it.type == T::class }?.get() as? T ?: error("Class ${T::class} not specified") }

  private inline fun <reified T: Any> factory(noinline constructor: () -> T) = Factory(T::class, constructor)

  private inline fun <reified T: Any> single(noinline constructor: () -> T) = Single(T::class, constructor)

  interface Item
  {
    val type:      KClass<out Any>
    val construct: () -> Any

    fun get(): Any
  }

  private data class Single(override val type: KClass<out Any>, override val construct: () -> Any) : Item
  {
    private val instance: Any by lazy(construct)

    override fun get(): Any = instance
  }

  private data class Factory(override val type: KClass<out Any>, override val construct: () -> Any) : Item
  {
    override fun get(): Any = construct()
  }
}