Surprise symbol resolve

I am working on a Java plugin and wondered how Kotlin resolved a var reference from an anonymous class, which is different behavior from Java. This was surprising to me:

fun foobar() : Base {
    val zap = 1
    return object : Base() {
        val zap = 2
        fun hmm(): Int {
            return zap   // 1
        }
    }
}

The reference to zap resolves to the local in foobar(), not the property in the anon class. It’s surprising, but at the same it addresses the issue I am researching. Basically, the Java way resolves how you’d expect, which is to select the “closest” one. This approach, however, leaves you with no way to access foobar’s local zap. I tend to favor the least surprising behavior, but this is a tough one.

3 Likes

I’m pretty sure it’s a bug. I can’t think of any other reason for this behaviour and as far as I can tell it isn’t documented anywhere. I took the liberty to create an issue for this on the koltin bug tracker: https://youtrack.jetbrains.com/issue/KT-45207

1 Like

You can use this.zap.

(Interestingly, I can’t find a way to explicitly refer to the outer zap, so if it defaulted to the nearest one — which would, as you say, be the more intuitive option — there would be no way to refer to the outer one at all. So this awkward default does at least allow you to refer to both.)

You can’t? I would expect that you can use labels to access it zap@foobar.

1 Like

Yeah, I expected that, too — but I tried it before posting, and couldn’t find anything that worked!⠀Our expectations aren’t always justified :slight_smile:

1 Like

Apparently using this.zip produces the expected result though:

fun foobar() : Base {
    val zap = 1
    return  object : Base() {
        val zap = 2
        override fun hmm(): Int {
            return this.zap   // 2. Try changing this back to just zap!
        }
    }
}

abstract class Base {
    abstract fun hmm(): Int
}

fun main() {
    println(foobar().hmm())
}
1 Like

This looks intentional. When I said it leaves you with no way to access foobar’s local zap, I was referring to the Java resolution rules. With Kotlin you can use zap to target the local or this.zap for the member; it may be counterintuitive, but with Java there’s no way to access the local.

I’m facing this exact issue with a Java plugin project and considering doing exactly what Kotlin does.

2 Likes

While of course the case needs to be handled correctly, this is a case of name shadowing that is generally considered poor coding. Partially because it is very easy to get confused as to the meanings (and what the compiler does).

Re poor coding, absolutely! But the pattern comes up with some exotic code generators I’m wrestling with. Anyhow, I prefer Kotlin’s way since it enables access to a shadowed symbol, which should always be possible in my view.

This issue also looks similar: https://youtrack.jetbrains.com/issue/KT-38372