Kotlin在子类中进行惰性初始化

Joh*_*eer 2 lazy-initialization kotlin

我正在尝试构建一个包含在子类中初始化的属性的字符串.

我读到了懒惰的初始化,但不知怎的,这不像我预期的那样工作.

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"
}
Run Code Online (Sandbox Code Playgroud)

既然我已经覆盖了packageName()processName()性质,我希望上调用packageName,它将使用从子类的实现财产.

但是,当我打电话给processFullName财产时,它会抛出一个java.lang.NullPointerException.

val builder = WorkerFullNameBuilder(JmsDirectionEnumeration.ESB_IN, TechnicalDomainEnumeration.INFOR, "ccmd")
val name = builder.processFullName
Run Code Online (Sandbox Code Playgroud)

如何以正确的方式初始化packageName和processName属性?

zsm*_*b13 5

这是在构造函数调用非final方法从而访问未初始化的变量的情况.

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

val processFullName: String = "$moduleName/$packageName.$processName"
Run Code Online (Sandbox Code Playgroud)

要获取两个惰性属性的值,这将调用抽象方法,其中packageName()引用jmsDirectioncdmCode返回其值 - 这些属性尚未初始化,因为它们的值是超类构造函数运行设置的.这是子类'构造函数的简化版本,反编译回Java:

public WorkerFullNameBuilder(@NotNull JmsDirectionEnumeration jmsDirection, @NotNull TechnicalDomainEnumeration technicalDomain, @NotNull String cdmCode) {
    super(technicalDomain);
    this.jmsDirection = jmsDirection;
    this.cdmCode = cdmCode;
}
Run Code Online (Sandbox Code Playgroud)

作为演示,如果您不参考这些,例如,如果您在两个子类方法中都返回常量,那么您的代码实际上可以正常运行:

override fun packageName() = "foo"
override fun processName() = "Worker"
Run Code Online (Sandbox Code Playgroud)

但是,你在这里需要的解决方案最有可能使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

}
Run Code Online (Sandbox Code Playgroud)