Access private supertype callable from within nested class

Consider the following code:

open class X {
    private fun m() = Unit

    class Y : X() {
        fun n() {
            X().m()   // 1
            m()       // 2
            super.m() // 3
        }
    }
}

So (1) is ok, as expected. I would also expect (2) to be ok, but there is a compiler error along the lines “Cannot access ‘fun m(): Unit’: it is private in supertype X”. Interestingly, (3) also compiles.

I wonder whether this is intended, or maybe a compiler bug. At least the behavior is not derivable from the Kotlin spec.

This is expected. The code above is the same as:

open class X {
    private fun m() = Unit
}

class Y : X() {
    fun n() {
        X().m()   // 1
        m()       // 2
        super.m() // 3
    }
}

As you can see, m() is attempting to call X.m(), which is a private method of a super-type. This is not legal, as expected.

Just guessing, but if you’re coming from Java, you may be confusing nested classes (static class in Java, class in Kotlin) and inner classes (class in Java, inner class in Kotlin). With an inner class, your code compiles, though it’s extremely hard to understand due to having two different instances of X in scope with different state.

But that code isn’t quite the same, is it? As you say, Y is not an inner class — but in the question, it is a nested class. And so doesn’t Y get access to the internals of its outer class?

No, I think the reasons that m() fails are:

  • Unqualified m() refers to the outer class method, not that of the superclass (even though they’re the same code).
  • m() is an instance method, so needs an instance of X to receive a call.
  • Y is a nested class, and so (unlike an inner class) is not associated with an instance of X.

So the call is invalid because there’s no instance to receive it, not because the method is private.

(You can demonstrate this by making Y an inner class; it then compiles fine.)

With regards to unqualified m(), both examples are the same.

With nested class:

  • Playground
  • Error: Cannot access 'm': it is invisible (private in a supertype) in 'Y'

With two different classes:

  • Playground
  • Error: Cannot access 'm': it is invisible (private in a supertype) in 'Y'

The error message is clear that the compiler is attempting to call the super-method, not the one from the outer class.

And yes, it can’t refer to the outer class (and doesn’t even attempt to) because it doesn’t have an instance of it.

You can demonstrate this by making Y an inner class; it then compiles fine.

That’s why I mentioned inner classes, yes.

The example is intended to be nested and not-inner. The variant where Y is outside is not the same, as then (1) also does not compile anymore. But as nested class, (1) does compile and that’s logical because m is visible inside X and also Y is inside X. The philosophical question is, why should this be different when m is accessed “through inheritance”. I don’t see why the rules of visibility should be dependent on the rules of inheritance.

In the meantime, I noticed that (3) throws a runtime IllegalAccessError. So there is at least one bug involved, for which I created a ticket:
KT-72621 Errors when accessing private supertype callable from within nested class

Maybe the deeper reason for these problems are on Java bytecode level.