Cannot access protected method declared in super class


#1

Hi
According to docs protected - visible inside this class only + visible in subclasses too

But I got compile error i the following code:

abstract class AbstractClass{
    protected fun method() = Unit
}

class Child : AbstractClass(){
    private val subChild: AbstractClass = Child()

    fun someMethod() = subChild.method() // Kotlin: Cannot access 'method': it is protected in 'AbstractClass'
}

It’s unexpected for me, because, as I understand from the docs, it should works like in Java

public abstract class AbstractClass {
    protected void method(){
    }
}

class Child extends AbstractClass{
    private AbstractClass subChild = new Child();

    private void someMethod() {
        subChild.method(); // works ok
    }
}

So, is it a bug or expected behavior?


#3

Is it is, it should not be that way. Protected means visibility modifier, while open means possibility to override. Calling protected method should be possible even if it is not open.


#4

Looks like a bug. Child().method() works, while (Child() as AbstractClass).method() doesn’t.


#5

Agree. In my real case the method() is abstract and taking a role of removing the code duplication and some service staff. I don’t want it to be visible for outside of AbstractClass family.


#7

Please, read my comment above.


#8

Yes, you’re right. I misunderstood the question. Shame on me …


#9

I think the problem here is that you are accessing the protected member of a member not parent. Java does not make the distinction, but based on what you say here, Kotlin does (possibly by design).

Normally the purpose of the protected visibility is to provide an additional interface to child classes. In this particular case you are using this api not to act as subclass (the same object instance), but to access elements of a different instance. In effect you are using protected as a way to circumvent the limitations of the visibility system. Friend declarations would probably be more sustainable here. The idiomatic way of doing it in Kotlin is internal visibility, but I agree that depending on module size this can increase the target size a lot.

Internal does however mean that you don’t introduce the method in the binary interface of the class (from an ABI perspective protected is just as public as public). In that sense internal is “better” even though the method involved is exposed inside the entire module.


#10

Make sense. But look at the next example:

abstract class AbstractClass{
    protected fun protectedMethodAbstractClass() = Unit
}

class Child : AbstractClass(){

    fun testMethod(){
        (Child() as AbstractClass).protectedMethodAbstractClass() // [1] Kotlin: Cannot access 'method': it is protected in 'AbstractClass'

        Child().protectedMethodAbstractClass() // [2]
        Child().privateMethod() // [3]
    }

    private fun privateMethod() = Unit
}

If kotlin visibility has instance context(the line marked as [1]), then I totally don’t understand why [2] and [3] should work.
If [2] works fine, then [1] should works as well. As for me the behavior is not intuitive.
I just want to know is it the bug or expected behavior. If expected, I want to know why.
Once again, There is no explanation about it at docs


#11

I would say it is probably a bug. Looking at 2 and 3 protected should never be more restrictive than private. At the same time if some sort of “friend” declarations were available, private could be restricted to the current instance only (where the friend declarations can create the gaps needed). Using private members of overridden classes can easily break things if not carefully designed for inheritance (say a field is shadowed by an override of the getter).


#12

There are few similar reports in YT.
I found the same issue in YT, but it already resolved(marked as duplicate of another one issue that already fixed)
So, I created one new report


#13

Very interesting. Especially the duplicate of the original report. I must say I didn’t know that the JVM was that pedantic about things. I guess that it will compile if you cast to the type itself:

(GrandChild() as Child).protectedMethodAbstractClass()

This would meet the “protected as strict superset of private” requirement but would also restrict the applicability of protected outside the instance hierarchy.