访问Kotlin属性时的java.lang.StackOverflowError

时间:2019-04-11 16:41:15

标签: kotlin

我从Packt的“ Programming Kotlin”中获得了这个(人为)示例,该示例使用具有继承性的辅助构造函数。

  

编辑:从答案中可以明显看出问题出在backing field上。但是这本书并没有介绍这个想法,只是用了错误的例子。

open class Payment(val amount: Int) 

class ChequePayment : Payment { 
    constructor(amount: Int, name: String, bankId: String) :  super(amount) { 
        this.name = name
        this.bankId = bankId 
    }

    var name: String
        get() = this.name
    var bankId: String
        get()  = this.bankId
} 


val c = ChequePayment(3, "me", "ABC")    
println("${c} ${c.amount} ${c.name}")

当我运行它时,显示此错误。

$ kotlinc -script class.kts 2>&1 | more
java.lang.StackOverflowError
    at Class$ChequePayment.getName(class.kts:10)
    at Class$ChequePayment.getName(class.kts:10)
    at Class$ChequePayment.getName(class.kts:10)

第10行似乎是无限递归,如何解决?

2 个答案:

答案 0 :(得分:6)

您的代码中有一个递归:

class ChequePayment : Payment { 
    constructor(amount: Int, name: String, bankId: String) :  super(amount) { 
        this.name = name
        this.bankId = bankId 
    }

    var name: String
        get() = this.name // recursion: will invoke getter of name (itself)
    var bankId: String
        get()  = this.bankId // recursion: will invoke getter of bankId (itself)
} 

如果您不需要为自己的吸气剂定制逻辑,只需保留以下属性即可:

var name: String
var bankId: String

他们将有一个默认的getter,它只返回返回后备字段的值。

注意:可以/应该将其重构为:

class ChequePayment(amount: Int, var name: String, var bankId: String) : Payment(amount) {
    // ...
}

这使用主要的构造函数,并且冗余度要低得多。

答案 1 :(得分:3)

要访问后备字段,您必须使用关键字field而不是this.name,请参阅https://kotlinlang.org/docs/reference/properties.html#backing-fields

如前所述,

this.name引用了getter,后者引用了this.name,这是一个无限递归。在代码中:

var name: String
    get() = field
var bankId: String
    get()  = field

旁注:Android Studio和Idea将正确地抱怨您在这种情况下不需要吸气剂。因此,您可以进一步简化:

var name: String
var bankId: String