Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite recursion in Getter in Kotlin

Tags:

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.

like image 945
O.O. Avatar asked Jan 18 '18 18:01

O.O.


2 Answers

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.

like image 93
hluhovskyi Avatar answered Sep 23 '22 21:09

hluhovskyi


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

like image 26
LeoColman Avatar answered Sep 22 '22 21:09

LeoColman