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?
I don’t get the use case for this, care to elaborate? If you’re doing some lazy initialisation you can use lazy.
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.
Not a design bug
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”.
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
}
}
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.
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.
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.