1

I got this (contrived) sample from Packt's "Programming Kotlin" on using secondary constructor with inheritance.

Edit: from the answer it is clear that the issue is about backing field. But the book did not introduced that idea, just with the wrong example.

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}")

When I run it this error is shown.

$ 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)

Line 10 does seems to be a infinite recursion, how to solve it?

leesei
  • 6,020
  • 2
  • 29
  • 51
  • too bad that Packt's material contain non-working examples – leesei Apr 11 '19 at 17:09
  • Ouch. That's not a good sign... additionally, I know this is for the purpose of an example, but you probably would use a primary constructor for this. – Salem Apr 11 '19 at 17:13
  • 1
    Possible duplicate of [What's Kotlin Backing Field For?](https://stackoverflow.com/questions/43220140/whats-kotlin-backing-field-for) – glee8e Apr 13 '19 at 11:21
  • Kinda, but the error is cryptic for a newbie not familiar with the idea of field keyword (or the property instead of field in Kotlin). The links does helps clarify that. – leesei Apr 14 '19 at 17:21

2 Answers2

6

You have a recursion in your code:

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)
} 

If you don't need custom logic for your getter, just leave your properties like this:

var name: String
var bankId: String

They will have a default getter, which does nothing more than returning the value of the backing field.

Note: The code as it is can/should be refactored to this:

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

This uses the primary constructor and is much less redundant.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
4

To access the backing field you have to use the keyword field instead of this.name see https://kotlinlang.org/docs/reference/properties.html#backing-fields

this.name references the getter, which references this.name which is an infinite recursion, as you already noted. In code:

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

Side note: Android Studio and Idea will complain rightfully that you don't need a getter in this case. So you can simplify even more:

var name: String
var bankId: String
leonardkraemer
  • 6,573
  • 1
  • 31
  • 54