Kotlin 继承和变量可见性

Kotlin inheritance and variables visibility

我遇到这种情况:

abstract class BaseWebClient(baseUrl: String) {

    abstract val defaultHeaders: HttpHeaders

    val client = WebClient
        .builder()
        .baseUrl(baseUrl)
        .defaultHeaders { it.addAll(defaultHeaders) }
        .build()
}

class AnimalsPortalClient(val config: Config, baseUrl: String) : BaseWebClient(baseUrl) {

    override val defaultHeaders: HttpHeaders
        get() {
            val headers = HttpHeaders()
            headers.add("app-name", config.appName)
            headers.add("app-version", config.appVersion)

            return headers
        }

    fun getAnimals(): String {
        return client.get( // ... etc
    }
}

此解决方案不起作用,因为 - 当尝试从派生 class 中的覆盖 属性 检索 defaultHeaders 时 - 变量 confignull.

一个可能的解决方案是将 config object 传递给基础 class' 构造函数:

abstract class BaseWebClient(val config: Config, baseUrl: String) { 

    abstract val defaultHeaders: HttpHeaders
    
    // ... etc
}

class AnimalsPortalClient(localConfig: Config, baseUrl: String) : BaseWebClient(localConfig, baseUrl) {
    
    override val defaultHeaders: HttpHeaders
        get() {
            val headers = HttpHeaders()
            headers.add("app-name", config.appName)
            headers.add("app-version", config.appVersion)

            return headers
        }
    
    // ... etc
}

但此解决方案有一个缺点:并非所有扩展 classes 都需要配置 object。在大多数派生 class 中,我有空的默认值 headers。像这样:

class SoccerPortalClient(baseUrl: String) : BaseWebClient(baseUrl) {

    override val defaultHeaders: HttpHeaders
        get() = HttpHeaders()

使用我提出的解决方案,我将被迫始终有一个配置 object 传递给基础 class,即使不需要它。

所以基本上:

  1. 我对这个行为有点疑惑:为什么变量为空?是可见性问题,还是...?
  2. 解决这个问题的正确实施是什么?

谢谢!

问题是您在基础 class 中的 client 字段在其他任何事情之前被初始化。您可以延迟初始化它。像这样:

val client by lazy {
    WebClient
        .builder()
        .baseUrl(baseUrl)
        .defaultHeaders { it.addAll(defaultHeaders) }
        .build()
}

您正在尝试访问 defaultHeaders 属性,此时派生 class 尚未初始化。

考虑将 client 属性 初始化程序转换为 getter:

val client
    get() = WebClient
        .builder()
        .baseUrl(baseUrl)
        .defaultHeaders { it.addAll(defaultHeaders) }
        .build()

或使用lazy delegate:

val client by lazy {
    WebClient
        .builder()
        .baseUrl(baseUrl)
        .defaultHeaders { it.addAll(defaultHeaders) }
        .build()
}