How can one access Kotlin's `backtickedFunctions` from Java? - java

TL;DR:
Is there a simple syntax in java to access kotlins backticked functions such as fun `if`(){...}
Long Version:
In Kotlin, one may write the following class.
class ShapeShifter {
fun speak() { println("Hello fellow hooman") }
fun `speakLikeA🐱`() { println("Meow") }
fun `speakLikeA🐶`() { println("Bwoof !") }
fun `speakLikeA🐧`() { println("NOOT NOOT ! (you would'nt have so much problems with linux ...)") }
}
And this would work just fine ... All your fellow kotlin-ers would be able to speak to you in all your forms like so :
ShapeShifter().`speakLikeA🐶`() // would work like a charm
but when interracting with java-ist muggles your secret identity would be safe because I am pretty sure java-ists can only interract with you like so :
new ShapeShifter().speak()
My question is :
Is there a way for java commoners to reach to your backticked kotlin functions WITHOUT resorting to using black magics such as introspection/reflection like so :
var tomJedusor = new ShapeShifter();
ShapeShifter.class.getDeclaredMethod("speakLikeA🐍").invoke(tomJedusor); //the forbidden arcane spell banned from Konoha ...

#JvmName annotation lets you define jvm name for that method.
#JvmName("speakLikeACat")
fun `speakLikeA🐱`() { println("Meow") }
#JvmName("speakLikeADog")
fun `speak like a 🐶`() { println("Bwoof") }
Now, you can access that method from java code.
// .java
shapeShifter.speakLikeACat();
shapeShifter.speakLikeADog();
Accesing backticked kotlin methods, fields from java is only possible with some jvm annotations. Kotlin compiler must obey java language specifications to generate the byte code. Actually java language specification does not allow this feature.

Related

Call a Kotlin suspend function from Java

I have a Kotlin library that I'm attempting to call from Java. I haven't worked with Kotlin before.
The Kotlin library function is as follows:
suspend fun decode(jwt: String): UsefulThing {
// does a bunch of stuff, removed for brevity.
return otherthing.getUsefulThing(jwt)
}
How can I call this from Java? So far I've tried:
Continuation<UsefulThing> continuation = new Continuation<>() {
#NotNull
#Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
#Override
public void resumeWith(#NotNull Object o) {
System.out.println("Result of decode is " + o);
}
};
// Call decode with the parameter and continuation.
Object result = UsefulThingKt.decode(JWT, continuation);
// result is COROUTINE_SUSPENDED
I never see any console output. Looks like the continuation is never called, or it's run in another context. I've pored over other answers and coroutines seem to have gone through a number of iterations - I can't find an explanation that really makes sense to me.
I should note that I'm running on Java 11.
How can I simply call the kotlin function?
I suggest to not even try. Suspend functions were never meant for Java interop.
Instead, convert it on the Kotlin side to something that Java understands - to CompletableFuture:
fun decodeAsync(jwt: String): CompletableFuture<UsefulThing> = GlobalScope.future { decode(jwt) }
We can freely mix Java and Kotlin code in a single module, so you can create such wrapper inside your project.
Depending on your case you could use GlobalScope (in Java we don't have structured concurrency) or you could create a custom CoroutineScope and handle its lifecycle manually.

How do you access Kotlin Library Sealed Classes as Error Handling in Java?

I've imported a library into my code that uses Sealed Classes as Error Handling. The Library is written in Kotlin, and my code is in Java. Other than this line, things have gone okay.
Code Example of what I've tried to even hold the Resource:
String dogID = "1234";
DogClient dogClient = new dogClient(); //params not important.
Resource<DogDto> dogDtoResource = dogClient.fetchDog(dogID); //Problem Statement
The dogClient.fetchDog(String id) method uses a sealed class called Resource where it uses data classes to handle errors. When I try to do the above, it says it cannot access Kotlin.coroutines.Continuation.
Resource in T code:
sealed class Resource<in T> {
data class Success<T>(val data: T) : Resource<T>()
data class Error(val exception: Throwable, val statusCode: Int?) : Resource<Any>()
}
I need to access the data on Success, and know when it throws an Error. The code in Kotlin would work something like this:
when(dogClient.fetchDog(dogId)) {
is Resource.Success -> result.data;
is Resource.Error -> throw new Exception();
I am completely lost on how to translate this to Java and haven't found any articles/documentation to help me.
it says it cannot access Kotlin.coroutines.Continuation
The problem is probably not the Resource sealed class then, but rather the fetchDog function you're trying to call is most likely a suspend function in Kotlin (using Kotlin coroutines).
You can check this other answer for this specific problem. It basically boils down to providing a non-suspend function from the Kotlin code, which you will be able to call from Java.
If you cannot modify the library, you can add a simple Kotlin file to your project to write this "bridge" function (but this means you'll need to setup Kotlin compilation in your project).

Translate Kotlin interface to Java

I am using this library which is a CalendarView. https://github.com/kizitonwose/CalendarView
In the sample code there is the following code which attaches a Scroll Listener to the CalendarView
calendarView.monthScrollListener = { // etc}
I am unsure how to translate this to Java, I try the following but the "MonthScrollListener" class is nowhere to be found, its like it want some other type but I cannot find the type. Everything else has worked so far when translating the Kotlin to Java but I cannot see how this might work
mBinding.calendarView.setMonthScrollListener(new MonthScrollListener(){ // etc});
What should I pass into the setMonthScrollListener() method?
Edit: when I "ctrl click" on the setMonthScrollListener() it takes me into the CalendarView class and there is the following line:
public final var monthScrollListener: com.kizitonwose.calendarview.ui.MonthScrollListener? /* = ((com.kizitonwose.calendarview.model.CalendarMonth) -> kotlin.Unit)? */ /* compiled code */
So I try explicitly referencing the MonthScrollListener but everything is resolved up to the MonthScrollListener, which isnt available...
typealias is not visible in Java, but given the example you're talking about is:
typealias MonthScrollListener = (CalendarMonth) -> Unit
Then in Java world it should be similar to single method interface like (more about it below):
import kotlin.Unit;
interface MonthScrollListener {
Unit whatever(CalendarMonth cm);
}
It could be void because this is what Unit means in Kotlin but you know - life.
So passing Lambda in that method which expects listener should look like:
whatever.setMonthScrollListener((CalendarMonth cm) -> {
// w00t
return kotlin.Unit.INSTANCE;
});
I've basically ended up writing the same approach as suggested by #MishaAkopov
Edit (after reading about it):
But what type is it actually? It appears that Kotlin standard library has a bunch of interfaces like Function0<R> and Function2<P1,P2,R> that have one method invoke. So if you'd need to use above Kotlin code in previous Java versions it would instead look like:
Function1<CalendarMonth, Unit> listener = new Function1<CalendarMonth, Unit>() {
#Override
public Unit invoke(CalendarMonth cm) {
// Do something with calendar month
return kotlin.Unit.INSTANCE;
}
}
whatever.setMonthScrollListener(listener);

Wrap a method in commands using Annotation Processing

I have a bunch of tasks that need to be performed before and after a method call.
My approach is to use annotation processing, and a custom annotation to do this.
#BeforeSomething(task = 'something')
#AfterSomething(task = 'something else')
fun something(blah: Something) {
Log.d("Something", "Something $blah")
}
So that the end output from just running this method could be like:
Before task, options 'something'
Something blahblahblah
After task, options 'somethign else'
Whats the best way to do this? I've looked into AspectJ but thats not working with Kotlin. I've also looked into Lombok, but I can't use that library.
You could provide the tasks via a higher order function - a wrapper function:
inline fun <T> wrapFunction(before: Task? = null, after: Task? = null, body: () -> T): T =
try {
before?.run()
body()
} finally {
after?.run()
}
fun something(blah: String) = wrapFunction(before = BeforeTask, after = AfterTask) {
println("Something")
}
This requires no extra tools and is refactoring safe.
If you not provide a before or after parameter the Kotlin compiler will not even generate the null-check, due to dead code elimination (abstractions without costs).

How to call Kotlin standard library function from Java?

Motivation:
In our Android project we have many verifications like str != null && !str.isEmpty(), so I decided to refactor them to a helper method.
For a moment I use following class as a helper:
public class StringUtil {
public static boolean isNullOrEmpty(#Nullable String str) {
return str == null || str.isEmpty();
}
}
Problem:
We already have a string's helper class, written in Kotlin (say, String.kt).
So, this is not clear to have two helpers (one in Java and one in Kotlin).
What I tried:
Naive approach to copy-past isNullOrEmpty() inside String.kt do not successed, because $reciever is null, so it crashed.
Secondly, I tried to used Kotlin native isNullOrEmpty() from kotlin.text (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/is-null-or-empty.html):
public inline fun CharSequence?.isNullOrEmpty(): Boolean
but I cannot figure out how to call it from Java. This page (https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html) do not provide any suggestions.
The problem is not about Accessing Kotlin extension functions from Java. My extension is perfectly visibly, but it crash because of null-receivier. As I mentioned below, question is more about accessing native library code, not my own extension.
Any help please ?
Some standard library functions, including this one, are marked with the #InlineOnly annotation, which makes them inaccessible from Java. For most other functions, you can access them exactly as described in the question linked as a duplicate.
After suggestion from #yole, I finally manage to convert code correctly to extension:
fun String?.isNullOrEmpty(): Boolean = (this == null || this.isEmpty())
So, problem was in defining extension as String, not as String? which leads to crash on $receiver. (Kotlin generates hidden not-null verifications when translating to Java, see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#null-safety)
Alternatively, standard library functions marked with #InlineOnly can be accessed from Java via custom extension.
fun String?.isVoid(): Boolean = this.isNullOrEmpty()
My problem here was in misunderstanding of ?. I tried this first:
fun String?.isVoid(): Boolean? = this?.isNullOrEmpty()
As #Moria mentioned, please note behaviour of ? - b?.method will return b.method if b is not null, and null otherwise (https://kotlinlang.org/docs/reference/null-safety.html#safe-calls)

Categories

Resources