如何在 Kotlin 中访问继承的构造函数参数

问题描述 投票:0回答:2

我正在处理一个 kotlin 示例项目。其中一项任务是实现一个可以跟踪坐标的游戏板类。

提供的接口如下所示:

interface SquareBoard {
    val width: Int

    fun getCellOrNull(i: Int, j: Int): Cell?
    fun getCell(i: Int, j: Int): Cell
    fun getAllCells(): Collection<Cell>

}

interface GameBoard<T> : SquareBoard {

    operator fun get(cell: Cell): T?
    operator fun set(cell: Cell, value: T?)

}

我实现的类如下所示:

open class SquareBoardImpl( override val width: Int) : SquareBoard {

    private val cells:List<Cell>

    init {
        val coords = (1..width).toList()
        fun pairs(i: Int) = coords.withIndex().map { j -> Cell(i, j.value) }

        this.cells = coords.flatMap(::pairs)
    }

    override fun getCellOrNull(i: Int, j: Int): Cell?
    {
        return cells.find { cell-> cell == Cell(i,j) }
    }

    override fun getCell(i: Int, j: Int): Cell {

        return getCellOrNull(i, j) ?: throw IllegalArgumentException()
    }
    override fun getAllCells(): Collection<Cell>
    {
        return cells;
    }
}

class GameBoardImpl<T>(override val width: Int) : SquareBoardImpl(width), GameBoard<T>
{
    var cellMap:MutableMap<Cell, T?> = getAllCells().associateWith { null }.toMutableMap()
    override fun get(cell: Cell): T? {
        return cellMap[cell]
    }

    override fun set(cell: Cell, value: T?) {
        cellMap[cell] = value
    }
}

fun createSquareBoard(width: Int): SquareBoard = SquareBoardImpl(width)
fun <T> createGameBoard(width: Int): GameBoard<T> = GameBoardImpl<T>(width)

一切都编译并运行,但我在 SquareBoardImpl 的 init 内的 IDE 中看到以下警告:

在构造函数中访问非最终属性宽度

对于这个简单的例子,我知道我可以简单地忽略警告,但我想知道处理这个问题的正确方法是什么。如何以安全的方式在构造函数中引用“宽度”?

kotlin class inheritance constructor
2个回答
0
投票

“在构造函数中访问非最终属性宽度”警告与您在

SquareBoardImpl
类的 init 块中使用 width 属性这一事实有关,并且它未标记为
final
。在 Kotlin 中,访问构造函数中的非最终属性可能会导致问题,因为子类可能会覆盖该属性,并且当超类构造函数运行时子类实现可能尚未准备好。

要解决此警告,您可以将

cells
字段的初始化移至惰性委托。在这种情况下,
cells
字段的初始化不会在构造函数中完成,而是在第一次访问时完成:

private val cells: List<Cell> by lazy {
    val coords = (1..width).toList()
    fun pairs(i: Int) = coords.withIndex().map { j -> Cell(i, j.value) }
    coords.flatMap(::pairs)
}

0
投票

这里的问题是

SquareBoardImpl
的子类可以覆盖
width
属性。如果这样做,将仅针对部分初始化的超类型执行
width
访问器的主体,这在大多数情况下是不期望的。

从您的代码看来,您实际上并不需要

width
保持开放,所以我建议将其定为最终版本:

open class SquareBoardImpl(final override val width: Int) : SquareBoard { ... }

class GameBoardImpl<T>(width: Int) : SquareBoardImpl(width), GameBoard<T> { ... }
© www.soinside.com 2019 - 2024. All rights reserved.