Search code examples
kotlin

Kotlin object expression property scope and shadowing rules


Why does the following code print 0?

class X {
    fun f() {
        val x = 0
        val a = object {
            val x = 1
            fun g() {
                println(x)
            }
        }
        a.g()
    }
}

fun main() {
    val x = X()
    x.f()
}

Why the inner property declaration does not shadow the outer one?


Solution

  • For an unqualified name, local callable (i.e. declarations in a statement scope) are always prioritised over non-local ones. The "things in inner scopes hides things in outer scopes" rule comes after that.

    From the overload resolution section of the spec,

    For an identifier named f the following sets are analyzed (in the given order):

    1. Local non-extension callables named f in the current scope and its upwards-linked scopes, ordered by the size of the scope (smallest first), excluding the package scope;
    2. The overload candidate sets for each pair of implicit receivers e and d available in the current scope, calculated as if e is the explicit receiver, in order of the receiver priority;
    3. [not relevant to this scenario]

    Step 1 considers all the local callables, and only after that do we try callables with a receiver in the second step.

    Applying this to your code,

    fun f() {
        val x = 0 // This 'x' is local to 'f'
        val a = object {
            val x = 1 // This 'x' is not local. The body of an object literal is a declaration scope, not a statement scope
            fun g() {
                println(x)
            }
        }
        a.g()
    }
    

    If locals are not prioritised like this, you'd have no way of referring to the local x when you're in g.