Search code examples
kotlininheritanceinterfaceextension-methods

Visibility of extension function defined in kotlin interface


I'm working on a sample project for the 'game of 15'. A cell on the game board is defined with:

package board

enum class Direction {
    UP, DOWN, RIGHT, LEFT;
}

data class Cell(val i: Int, val j: Int) {
    override fun toString()= "($i, $j)"
}

interface SquareBoard {
    val width: Int

    fun Cell.getNeighbour(direction: Direction): Cell?
}

interface GameBoard<T> : SquareBoard {

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

    fun find(predicate: (T?) -> Boolean): Cell?

}

I want to call that Cell.getNeighbor extension function from a function in the GameOf15 class

package games.gameOfFifteen

import board.Cell
import board.Direction
import board.GameBoard
import board.GameBoardImpl
import games.game.Game

class GameImpl(val initializer: GameOfFifteenInitializer) : Game
{
    val board:GameBoard<Int?>  = GameBoardImpl(4)

    override fun processMove(direction: Direction) {

        val openCell:Cell? = board.find { it == null}
        //this line does not compile
        val neighbor = openCell?.getNeighbor(direction)

    }
}

The above code does not compile. I get an error 'Unresolved Reference: getNeighbor'. I'm not sure what I'm doing wrong here. Is there something else I need to do to make the extension function visible?


Solution

  • Since it’s defined inside the interface, you can only use from within the context of that interface or something that implements it.

    Also, think about if you could call it from anywhere… how would the compiler know which implementation of it to use?

    Personally, I think it would make sense to define it as a traditional function so it’s easier to use. But you can use the extension function from outside the class with the help of scope functions like run:

    val neighbor = board.run { openCell?.getNeighbor(direction) }
    

    Since board is the receiver inside the lambda, you can call functions as if you are inside the Board class inside this lambda, so long as they are public.