【问题标题】:Kotlin lazy initialization in subclass子类中的 Kotlin 延迟初始化
【发布时间】:2018-06-20 07:05:14
【问题描述】:

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

我阅读了有关延迟初始化的信息,但不知何故,这并没有像我预期的那样工作。

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 属性?

【问题讨论】:

    标签: kotlin lazy-initialization


    【解决方案1】:

    这是calling a non-final method in a constructor 的情况,因此访问了未初始化的变量。

    在构造基类的时候,这行代码仍然是热切求值的:

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

    要获取两个惰性属性的值,这将调用抽象方法,其中packageName() 引用jmsDirectioncdmCode 以返回其值 - 这些属性尚未初始化,因为它们的值在超类构造函数运行后设置。这是子类构造函数的简化版本,反编译回 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
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-10
      • 1970-01-01
      • 1970-01-01
      • 2011-11-17
      • 1970-01-01
      • 1970-01-01
      • 2013-02-20
      相关资源
      最近更新 更多