简体   繁体   中英

Infinite recursion in Getter in Kotlin

I am familiar with Java, but I am having difficulty working with Kotlin.

To illustrate my question, here is some Java Code. If the getter finds the field to be NULL, it initializes the field, before returning the field.

package test;

public class InitFieldJava {

    private final static String SECRET = "secret";
    private String mySecret;

    public String getMySecret() {
        if(mySecret == null) initMySecret();
        return mySecret;
    }

    private void initMySecret() {
        System.out.println("Initializing Secret ....");
        mySecret = SECRET;
    }

    public static void main(String[] args) {
        InitFieldJava field = new InitFieldJava();
        System.out.println(field.getMySecret());
    }
}

Can I do something like the above in Kotlin. My attempt in Kotlin looks like this:

package test

class InitFieldKotlin {
    private val SECRET = "secret"
    private var mySecret: String? = null
    get() {
        if (mySecret == null) initMySecret() //Infinite Recursion!!!
        return mySecret
    }

    private fun initMySecret() {
        println("Initializing Secret ....")
        mySecret = SECRET
    }

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val field = InitFieldKotlin()
            println(field.mySecret)
        }
    }
}

My problem is that this results in infinite recursion:

Exception in thread "main" java.lang.StackOverflowError
    at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
    at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
    at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
    at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)

I'd appreciate knowing what I'm doing wrong.

Try to use field keyword inside get() :

private var mySecret: String? = null
    get() {
        if (field == null) initMySecret() 
        return field
    }

Generally speaking, field allows to access your value directly without calling get , almost in the same way as in your Java example. More information can be found in documentation .

The problem you're facing is that when you call your property this way, the getter will be called again. And when you call getter, another getter is called, and so on until an StackOverflow.

You can fix this as shown by @Google, and using field inside the getter, instead of the property name:

if (field == null)initMySecret()

This way you won't access the property using its getter.


But more importantly: why don't you use a lazy initialization ? If the variable is final, and it seems to be, you could use a lazy val

This way, the field won't be nullable anymore, so you won't have to safe-call it. And you'll not use boilerplate code, Kotlin can do this lazy initialization for you!

val mySecret: String by lazy {
    println("Initializing Secret. This print will be executed only once!")
    "SECRETE" //This value will be returned on further calls
}

More examples on Lazy can be seen at Kotlin Docs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM