Accessing Kotlin Extension Functions from Java

Accessing Kotlin extension functions from Java

All Kotlin functions declared in a file will be compiled by default to static methods in a class within the same package and with a name derived from the Kotlin source file (First letter capitalized and ".kt" extension replaced with the "Kt" suffix). Methods generated for extension functions will have an additional first parameter with the extension function receiver type.

Applying it to the original question, Java compiler will see Kotlin source file with the name example.kt

package com.test.extensions

public fun MyModel.bar(): Int { /* actual code */ }

as if the following Java class was declared

package com.test.extensions

class ExampleKt {
public static int bar(MyModel receiver) { /* actual code */ }
}

As nothing happens with the extended class from the Java point of view, you can't just use dot-syntax to access such methods. But they are still callable as normal Java static methods:

import com.test.extensions.ExampleKt;

MyModel model = new MyModel();
ExampleKt.bar(model);

Static import can be used for ExampleKt class:

import static com.test.extensions.ExampleKt.*;

MyModel model = new MyModel();
bar(model);

Unable to call kotlin extension function from java

The problem is that the receiving instance is encoded as a parameter. So:

fun String.extractDigits(strValue: String): String {...}

Becomes (javap output):

public static final java.lang.String extractDigits(java.lang.String, java.lang.String);

But you're passing only a single argument to the function.

I don't quite understand why you're using an extension function here, I'd expect to see the receiving instance used instead of passing a separate value:

fun String.extractDigits(): String {
val str = this.trim { it <= ' ' } // Using `this`, i.e. the receiving instance
var digits = ""
var chrs: Char
for (i in 0..str.length - 1) {
chrs = str[i]
if (Character.isDigit(chrs)) {
digits += chrs
}
}
return digits
}

Then, in Java, you can call it like you tried, and in Kotlin like this:

val str = "123blah4"
println(str.extractDigits()) // prints 1234

Unable to access Kotlin Extension function from Java in case it has a lambda as its parameter

Your lambda's type in Kotlin is ()->Unit, which Java sees as kotlin.Function0<kotlin.Unit>. So your Java lambda needs to return Unit instead of void as it currently does.

showWarningDialog(this, R.string.alert, R.string.leave_without_saving, () -> {
setResult(RESULT_CANCELED);
finish();
return Unit.INSTANCE;
});

should work. The Kotlin compiler inserts returning Unit for you, but Java doesn't treat Unit specially in any way.

If you just want to call the function from Java in one or a few places, this may be good enough; otherwise the way to make it convenient to call from Java is fun interface as shown in Andrii Hridin's answer.

How can I call Kotlin extension function with receiver from the Java static method?

You could just pass a random Object in:

MyExtensionsKt.log(new Object(), "This is a log message");

Of course, this will produce a log message that starts with Object, not the name of the current class.

To produce a log message that starts with the name of the current class, you would need to either create a new, temporary instance of the current class:

// suppose the static method is in "SomeClass"
MyExtensionsKt.log(new SomeClass(), "This is a log message");

or change the implementation of the log method to be a bit smart about its receiver. For example, if it is found that it is a Class, then just directly use its name as the prefix for the log message.

fun Any.log(message: String) {
val prefix = if (this is Class<*>) {
this.simpleName
} else {
this.javaClass.simpleName
}
println(prefix + message)
}

// then in SomeClass, you can do:

MyExtensionsKt.log(SomeClass.class, "This is a log message");

If you cannot do either of those things, then I would suggest that you not use a static method. How about the singleton pattern instead?

Example:

private static final SomeClass INSTANCE = new SomeClass();
public static final SomeClass getInstance() { return INSTANCE; }

// now you can have instance methods, and have access to "this"!

Side note:

Singletons are also how Kotlin's objects are implemented in the JVM.

object Foo {
fun foo() {
this.log("This is a log message")
}
}

When you call Foo.foo(), it may seem like you are calling a "static" method, but you are really just calling Foo.INSTANCE.foo(). Because of this, this is available in Kotlin objects.

Kotlin extension with parameters

Your approach to this problem is correct and it should work. For example, with this Kotlin code:

fun String.getStringBeforeLast(delimiter: String): String{
return this.substringBeforeLast(delimiter)
}

You should be able to invoke the function from Java like this:

ExtensionKt.getStringBeforeLast(str, ",");

If it didn't work then I guess there had to be some small mistake, like for example: you added delimiter param to getStringBeforeLast() function, but mistakenely tried to invoke getStringAfterLast() function from Java.

Also, you can always invoke functions of Kotlin stdlib directly from Java. Just note that substringBeforeLast()/substringAfterLast() actually receive one additional, optional parameter and you need to provide it from Java, making the code a little more verbose:

StringsKt.substringBeforeLast(str, ",", str);

Use Kotlin extension in android java class

You can mix kotlin and java ( use/call kotlin classes in java classes )
But what you want here is use a kotlin feature in java - this is not possible

Adding Kotlin extension methods to a pure Java library

Extension methods are compiled to static Java methods, for example from app.kt into the class AppKt, i.e. they are available to Java as well using AppKt.method(), as explained in the documentation.

Both your Java code and Kotlin code compile to Java Bytecode class files and can go into the same jar, i.e., no need to ship multiple jars. My personal build system of choice for building Kotlin/Java code is Gradle, but this is up to you.

How does a extension method work in Kotlin?

In Java you cannot extend a final class, but in Kotlin you can write
extension method to such final classes

I think you are assuming extension methods used inheritance which is not. It is rather a static method disguised in kotlin syntactic sugar. For example look at the below simple extension method:

String.removeSpace(): String {
return this.replace(" ", "")
}

You can think it as equivalent to the following java code.

static String removeSpace(String input) {
return input.replace(" ", "")
}

What kotlin does here is it provides wrapper on top so that by using this you can get the instance of the caller. By combining that with Kotlin's capability of define function as first class citizen, you can write elegant thing like this:

fun String.removeSpace() = this.replace(" ", "")


Related Topics



Leave a reply



Submit