8

I have this situation: There are a Java class

public class A {

    public void overrideMe(B param){
        //TODO: override me in Kotlin!
    }

    protected static class B {

    }
}

and a Kotlin class, which inherits from it and has to override method "overrideMe"

class K: A() {
    override fun overrideMe(param: B) {
        println("Wow!")
    }
}

But Kotlin doesn't allow this behaviour.

'public' function exposes its 'protected (in A)' parameter type B

Is there any way how to resolve this one?

P.S. It's not just a synthetic case - I faced this problem when I tried to implement custom Spring AmqpAppender and to override it's postProcessMessageBeforeSend method.

leonardkraemer
  • 6,573
  • 1
  • 31
  • 54

2 Answers2

2

There is no way to resolve this in Kotlin, and here is why:

The difference is that protected actually means something subtly different in Kotlin than in Java.

protected in Kotlin means:

kotlin protected: same as private (visible inside the file containing the declaration) + visible in subclasses too;

protected in Java means:

java protected: the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.

And with this knowledge the issue should be clear, the protected static class B in Kotlin is more like private static class B in Java. Therefore the warning is correct.

The Kotlin-Java Interop guide specifically states:

protected remains protected (note that Java allows accessing protected members from other classes in the same package and Kotlin doesn't, so Java classes will have broader access to the code);

Conclusion:

This means that Kotlin interprets the Java-protected as if it was a Kotlin-protected ergo there is no way to implement the class K in Kotlin as it is. The least you must do to make it work is create C extends A (in Java) that handles all public access of B and then extend this class in Kotlin. Like in this issue Calling protected static methods

The culprit:

The main problem is Javas behaviour of static nested classes, which

interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.

This convenient behaviour creates the problem in the first place.

Side note:

Probably the better match for Java-protected is Kotlins internal which provides a better level of encapsulation.

kotlin internal: any client inside this module who sees the declaring class sees its internal members;

leonardkraemer
  • 6,573
  • 1
  • 31
  • 54
  • But there is no "`protected static class B` in Kotlin" in the question: it is inherited from the Java `A` class. – Alexey Romanov Mar 15 '18 at 07:06
  • I see, unfortunately I don't think it is possible to extend `A` in Kotlin, see my update – leonardkraemer Mar 15 '18 at 12:31
  • 1
    This seems rather bogus to me; with Java, a subclass can reference a protected static inner class in the super class (e.g. in an overridden method parameter) with no problems. Surely Kotlin should do the same so that Kotlin users can interoperate with existing Java libraries, without forcing those libraries to make changes "just" for Kotlin compatibility. – Gary Russell Mar 15 '18 at 17:49
  • Maybe this will change, but looking at the threads (e.g. https://discuss.kotlinlang.org/t/kotlin-to-support-package-protected-visibility/1544 ) they seem pretty fond of their opinion. Maybe they will introduce something for this specific case in a later release. – leonardkraemer Mar 15 '18 at 17:58
  • 1
    @leoderprofi I had a short conversation with [Roman Elizarov](https://stackoverflow.com/users/1051598/roman-elizarov) yesterday. The conclusion is: "no way". – Nikolay Romanov Mar 16 '18 at 11:22
  • @NikolayRomanov "no way" that this will change? Or no way to do it in Kotlin? (both is what I think, too, looking at the facts) – leonardkraemer Mar 17 '18 at 00:59
0

Well, after all, the conclusion is: there is no way to solve this situation in pure Kotlin.

I hope that AmqpAppender.Event will become public in the nearest future.

Even if Java allows that behaviour, having no-public arguments in public methods seems like a bad design for me (also for the developers of Kotlin).