Kotlin的Getter无限递归

O.O*_*.O. 6 kotlin

我熟悉Java,但我在与Kotlin合作时遇到了困难.

为了说明我的问题,这里有一些Java代码.如果getter发现该字段为NULL,则在返回字段之前初始化该字段.

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以在Kotlin做类似的事情.我在Kotlin的尝试看起来像这样:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,这导致无限递归:

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)
Run Code Online (Sandbox Code Playgroud)

我很高兴知道自己做错了什么.

hlu*_*kyi 12

尝试在field里面使用关键字get():

private var mySecret: String? = null
    get() {
        if (field == null) initMySecret() 
        return field
    }
Run Code Online (Sandbox Code Playgroud)

一般来说,field允许直接访问您的值而无需调用get,几乎与Java示例中的方式相同.更多信息可以在文档中找到.


Ker*_*ker 7

您遇到的问题是,当您以这种方式调用您的财产时,将再次调用getter.当你调用getter时,会调用另一个getter,依此类推,直到调用StackOverflow.

您可以按@Google所示修复此问题,并field在getter中使用,而不是使用属性名称:

if (field == null)initMySecret()
Run Code Online (Sandbox Code Playgroud)

这样您就不会使用其getter访问该属性.


但更重要的是:为什么不使用延迟初始化?如果变量是最终的,而且似乎是,你可以使用懒惰val

这样,该字段将不再可以为空,因此您无需安全地调用它.并且你不会使用样板代码,Kotlin可以为你做这个懒惰的初始化!

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

关于Lazy的更多例子可以在Kotlin Docs上看到