2
votes

I'd like to have a Kotlin extension function for string that is accessible only in inheritors of MyParent class (protected function). MyParent class is written in java and cannot be converted to Kotlin. Is it possble to define a method in java code that can be used as extension function in Kotlin code?

I think I expect some kind of proper signature, or some magic @ThisIsExtenstionFunction annotation.

2

2 Answers

2
votes

Actually, you can. Non out of box, but it is possible ;)

Let's consider this example.

First, create a class MyParent in Java

public class MyParent {
}

And, define extension method in this interface:

interface MyParentExt {
    fun String.ext(importantParam: Int)
}

So, let MyParent implement this interface.

public class MyParent implements MyParentExt {

    @NonNull
    @Override
    public String ext(@NonNull String $receiver, int importantParam) {
        // do cool stuff
        return $receiver;
    }

    // For testing let's add other public method:
    public String ext(int importantParam) {
        return "Kotlin > Java";
    }

}

So, let's check what we can do in childs:

// Java child:
public class MyJavaChild extends MyParent {
    MyJavaChild() {
        "Java xD".ext(0); // Invalid! There is no such a method!
        ext("Java xD", 0); // Valid call for extension function
        ext(0); // Just Valid ;)
    }
}

// Kotlin class:
class MyGreatKotlinChild : MyParent() {
    init {
        "Kotlin is cool".ext(0) // Valid extension function!
        ext("Kotlin is cool", 0) // Invalid call. There is no method with such a signature!
        ext(0) // Valid.
    }
}

So, we can access our extension method in both, Java and Kotlin using lang-specified notation.

As one of your requirements you wanted this method to be protected. Unfortunately, visibility of interface methods is always public. So, you still should be able to call this function on a member.

But... Let's check how does it actually works ;)

// Java
public class Test {
    public static void main(String[] args) {
        MyChild child = new MyChild();

        // Both methods are valid
        child.ext("Java...", 0);
        child.ext(0);
    }
}

// Kotlin
fun main() {
    child.ext(0) // Normal function, valid call.
    child.ext("It actually works...", 0) // Hah, no way ;)

    // Just to be sure:
    "String".ext(0) // Nope, this ext method is visible for only class scope.
}

Also, your parent class just can extend ParentOfMyParent class where you would define your extension methods:

// Kotlin
abstract class ParentOfMyParent {
    protected abstract fun String.ext()
}

// Java
public class Parent extends ParentOfMyParent {
    @Override
    protected void ext(@NotNull String $receiver) {
    }
}

// Kotlin-child
class Child : Parent() {
    init {
        "".ext()
    }
}

// Java-child
public class ChildJava extends Parent {
    ChildJava() {
        ext("");
    }
}

This way method has a wantned visibility.

0
votes

There is no such annotation.

However, the compiler emits kotlin metadata annotations that might contain this information, but j would not recommend trying to trick the compiler into thinking some class is kotlin source rather than java source. More info: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-metadata/index.html