【问题标题】:kotlin factory for subclass子类的 kotlin 工厂
【发布时间】:2020-10-19 01:14:21
【问题描述】:

我有一个类需要一个工厂,当提供一个子类的样本值时,它将创建一个具有子类类的新对象。例如

open class Base(val i:Int){
    fun <T:Base>factory(sample:T, n:Int) = Base(i) // need sample." invoke constructor" (i) as T
}

关于如何在使用 Base 的任何子类作为参数调用而不需要添加样板文件或反射超出 KotlinJS 可用的反射以覆盖每个未来子类的工厂时如何使这项工作工作?

【问题讨论】:

  • 你需要使用反射来实例化类,并且有一个类契约,Base 的每个子类都必须有一个只有一个 n 参数的构造函数。这无法在编译时强制执行,因此当类中缺少此类构造函数时,您必须抛出异常。
  • @Tenfour04 ...是的,这也是我遇到的问题,看不到解决方法,希望可能使用注释。可惜,因为在每个子类中压倒一切的 fun factory 都变成了样板

标签: kotlin constructor annotations factory


【解决方案1】:
open class Base(val i: Int) {

    fun <T : Base> factory(sample: T, @Suppress("UNUSED_PARAMETER") n: Int): Base {
        @Suppress("UNUSED_VARIABLE") val constructor = sample::class.js

        return js("new constructor(n)") as Base
    }

    override fun toString() = "Base($i)"
}

class Derived(i: Int) : Base(i) {

    override fun toString() = "Derived($i)"
}


fun main() {
    println(Base(10).factory(Derived(20), n = 5))  // "Derived(5)"
}

【讨论】:

  • 我真的在追求一个解决方案,它使用的反射不超过 js 可用的反射,但至少这确实为 js 提供了一个解决方案,这意味着如果我使用带有期望/实际的多平台,我可以拥有jvm以及js代码。所以对 JS 使用 this 意味着此时只有 native 不满足。
【解决方案2】:

首先,你最好使用reified类型参数,这样你的工厂就不需要sample参数。 其次,如果您的工厂将返回一个泛型类型的对象,那么您将不需要在子类中重写它 - 它会返回所需类型的实例。

但是如果没有reflection,我看不出有什么办法:

open class Base (val i: Int) {
    inline fun <reified T : Base> factory(n: Int): T {
        val kClass = T::class
        val constructor = kClass.constructors.find { it.parameters.size == 1 && it.parameters[0].type == Int::class }
            ?: throw RuntimeException("Constructor with single Int parameter was not found for ${kClass.qualifiedName} class")
        return constructor.call(n)
    }
}

用法:

open class Derived(i: Int) : Base(i)
open class Derived2(i: Int) : Derived(i)

//any subclass of Base or Base itself when called on instance of Base
val base = Base(1)
val a: Base = base.factory(1) //or val a = base.factory<Base>(1)
val b: Derived = base.factory(1)
val c: Derived2 = base.factory(1)

//any subclass of Base or Base itself when called on any instance of Base subclass
val derived2 = Derived2(1)
val a1: Base = derived2.factory(1)
val b1: Derived = derived2.factory(1)
val c1: Derived2 = derived2.factory(1)

【讨论】:

  • 这个答案以“使用具体的类型参数”开始,以避免使用参数来指定类型。整个想法是工厂生成提供的类型的克隆,而不是当前类的实例! (每个问题都变成了钉子)如果您需要当前类的实例,为什么不只是实例一个:) 提供的代码确实提供了使用反射调用构造函数的可行代码,并且如果该代码应用于“样本”而不是当前类在诉诸反射时是可行的,但目标是避免诉诸反射。
猜你喜欢
  • 2016-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多