子类中的Kotlin延迟初始化

子类中的Kotlin延迟初始化,kotlin,lazy-initialization,Kotlin,Lazy Initialization,我试图用在子类中初始化的属性构建一个字符串 我读过关于延迟初始化的文章,但不知怎么的,这并不像我预期的那样有效 abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) { protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared" private val pack

我试图用在子类中初始化的属性构建一个字符串

我读过关于延迟初始化的文章,但不知怎么的,这并不像我预期的那样有效

abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {

    protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"

    private val packageName by lazy { packageName() }
    private val processName by lazy { processName() }

    val processFullName: String = "$moduleName/$packageName.$processName"

    protected abstract fun packageName(): String
    protected abstract fun processName(): String
}

class WorkerFullNameBuilder(
        private val jmsDirection: JmsDirectionEnumeration,
        technicalDomain: TechnicalDomainEnumeration,
        private val cdmCode: String) : SubProcessFullNameBuilder(technicalDomain) {

    override fun packageName() = "$moduleName.workers.${jmsDirection.value().toLowerCase()}.${cdmCode.toLowerCase()}"
    override fun processName() = "Worker"
}
由于我已经重写了
packageName()
processName()
属性,我希望在调用
packageName
属性时,它将使用子类中的实现

但是当我调用
processFullName
属性时,它抛出一个
java.lang.NullPointerException

val builder = WorkerFullNameBuilder(JmsDirectionEnumeration.ESB_IN, TechnicalDomainEnumeration.INFOR, "ccmd")
val name = builder.processFullName
如何以正确的方式初始化packageName和processName属性?

这是一种访问未初始化变量的情况

在构建基类时,这一行仍在热切地进行评估:

val processFullName: String = "$moduleName/$packageName.$processName"
要获取两个惰性属性的值,这将调用抽象方法,其中
packageName()
引用
jmsDirection
cdmCode
以返回其值-这些属性尚未初始化,因为它们的值是在超类构造函数运行之后设置的。下面是子类构造函数的简化版本,已反编译回Java:

public WorkerFullNameBuilder(@NotNull JmsDirectionEnumeration jmsDirection, @NotNull TechnicalDomainEnumeration technicalDomain, @NotNull String cdmCode) {
    super(technicalDomain);
    this.jmsDirection = jmsDirection;
    this.cdmCode = cdmCode;
}
作为演示,如果您不引用这些,例如,如果您在两个子类方法中都返回常量,那么您的代码实际上会运行良好:

override fun packageName() = "foo"
override fun processName() = "Worker"
但是,这里需要的解决方案很可能会使
processFullName
属性本身变懒,而不是使它使用的两个值变懒(不管怎样,您现在正在构造函数时对这两个值进行评估,因此您不会利用它们变懒)。这意味着您甚至不需要将这两个属性作为单独的属性:

abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {

    protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"

    val processFullName by lazy { "$moduleName/${packageName()}.${processName()}" }

    protected abstract fun packageName(): String
    protected abstract fun processName(): String

}