Should properties' getter be self recursive?


#1

Hey I love Kotlin lang ! It’s a pretty cool language , but I found a problem yesterday.
I just wanna create a property which can be only called once.
But it could cause StackOverflowError.
Here’s the simple code

`class Test {
    var flag = true
        get() {
            if (flag) {
                flag = false
                return true
            }
            return false
        }
        private set
}`

If I call Test().flag , I will get a StackOverflowError. for the self recursive of flag.

To fix it , I must use field instead of it’s real name.

class Test {
    var flag = true
        get() {
            if (field) {
                field = false
                return true
            }
            return false
        }
        private set
}

It looks unnatural and is difficult to read.
Should properties’ getter be self recursive not directly called backing field?


#2

I don’t get the use case for this, care to elaborate? If you’re doing some lazy initialisation you can use lazy.


#3

Thank you for your reading and answer.
Actually I just want to create a simple Timer class in my stupid game code.
Here’s the code in previous.

class Timer(val goalTime: Float) {
    var totalTime = 0f
        private set
    var finished = false
        private set
    var firstFinished = false
        get() {
            if (firstFinished ) {
                firstFinished = false
                return true
            } else {
                return false
            }
        }
        private set

    fun start() {
        totalTime = 0f
        finished = false
        firstFinished = false
    }

    fun update(deltaTime: Float) {
        totalTime += deltaTime
        if (totalTime >= goalTime) {
            finished = true
            firstFinished = true
        }
    }
}

Then in the core code, I called:

val timer=Timer(1f)

fun update(delta:Float){
    timer.update(delta)
    if(timer.firstFinished){
        //Do something
    }
}

When I called if(timer.firstFinished) I will get a StackOverflowError as the self recursive.

I just wondered why do I get a self recursive here.
I don’t think the getters designed to be self recursive is a good design.

Or in java lang:
I just want a thing like this:

public class Test {
	public boolean flag = true;
	public boolean getFlag() {
		if(flag){
			flag=false;
			return true;
		}
		return false;
	}
}

But I get this code in Kotlin generated:

public class Test {
	public boolean flag = true;
	public boolean isFlag() {
		if(isFlag()){
			setFlag(false);
			return true;
		}
		return false;
	}
	public void setFlag(boolean value){
		flag=value;
	}
}

I think it’s a design bug, isn’t it?
I apologize with my poor English.Thank you for your reading again.


#4

Not a design bug :slight_smile: That’s actually what it’s designed to do. Unlike Java, you don’t declare fields - you declare properties, and the implementation stuff you write is just what gets put into the accessors. In order to access the backing field, you reference it just like you mentioned in your first post - the second example that works, but looks “unnatural”.


#5

I’m assuming that you just want to execute a block code once when finished is true. I don’t think it’s a good design to put that very specific logic in the timer class, IMHO it should be:

val timer=Timer(1f)
var once = false
fun update(delta:Float){
    timer.update(delta)
    if(timer.finished && !once){
        //Do something
        once = true
    }
}

#6

Wouldn’t this be much simpler as:

var firstFinished = false
    get() {
        val result = field
        field = false
        return result
    }
    private set

Or if you want to get fancy the get could be:

    get() = field.apply { field = false }

You might want to consider adding synchronization to handle multiple threads.


#7

I’ve bumped into this same issue several times, scratched my head, and then fixed it. The “proper” answer should be for the Kotlin compiler to issue you a stern warning about a “recursive getter” and yellow-bulb offering to replace that with “field” or whatever else.


#8

There is already an issue about this: KT-1861.

I ran into this a couple of times myself and found it quite annoying. I can’t think of any good reason why recursion should be allowed here. If you really needed a recursive approach in your getter/setter you could always fall back to a recursive local function.