I have a library written in kotlin which i want to use in my Java program, but i don't know how to call an asynchronous method correctly.
/*This is my asynchronous kotlin method from the library*/
fun getNames(callback: (List<String>) -> Unit) = ...
/*In Java i want to call this method*/
List<String> names = getNames(...);
What parameter do i have to pass into the function in Java?
IntelliJ says i need something like this: 'kotlin.jvm.functions.Function1<? super java.util.List<String>,kotlin.Unit>' But i don't know how to instantiate such a class.
Ok, first of all give it a look at this great response from Kirill Rakhman.
Now, the result given by a callback from a asynchronous operation will come from the parameter. In this case it'll be the (List<String>) at the fun getNames().
A simple usage of this function in JAVA (8+) can look like this :
getNames(
strings -> {
strings.forEach(System.out::println);
return Unit.INSTANCE;
}
);
Related
Consider the following interface in kotlin:
LoginRepository.kt
interface LoginRepository {
suspend fun login(): LoginResponse
}
LoginRepo.java
class LoginRepo implements LoginRepository {
public Object login(#NonNull Continuation<? super LoginResponse> $completion) {
api.login((result) -> {
ContinuationUtilsKt.resumeContinuationWithSuccess($completion, result);
});
return null;
}
}
ContinuationUtils.kt
fun <T> resumeContinuationWithSuccess(cont: Continuation<T>, value: T) {
cont.resumeWith(Result.success(value))
}
I've attempted to drill down the code to its essential parts, i.e. a suspend function that is overridden in the java class makes an API call and returns a success or failure continuation using the continuation object and returns null.
However, the method LoginRepository.login when called returns null.
The overridden method signature in LoginRepo is generated by the IDE.
Am I supposed to return some object instead of null? Or something else that I'm missing.
I really don't think you're supposed to do this. The functions and classes used to implement it in Kotlin are internal/private and hidden from the Java side.
Basically, you need to intercept the original Continuation and resume the new returned Continuation with your return value. Then return Intrinsics.COROUTINE_SUSPENDED to indicate that you are not synchronously returning a value. If the return value is anything besides Intrinsics.COROUTINE_SUSPENDED, then I think it assumes you are directly returning the declared return value of the suspend function.
While this code may work, it probably doesn't handle all the edge cases, and it probably won't provide helpful stack traces in the event of a crash. The standard library implementation is far more complicated.
class LoginRepo implements LoginRepository {
public Object login(#NonNull Continuation<? super LoginResponse> $completion) {
Continuation<? super LoginResponse> cont = IntrinsicsKt.intercepted($completion);
api.login((result) -> {
ContinuationUtilsKt.resumeContinuationWithSuccess(cont, result);
});
return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
}
Kotlin interoperability with Java doesn't really include suspend functions. Suspend functions are very specific to Kotlin, they are hard to both invoke and to implement from Java.
In most cases I suggest to not even try to handle continuations and suspendable code from Java and instead create small "adapters" in Kotlin. These adapters would translate suspendable code to something more usable from Java. For example, in Kotlin it is pretty easy to convert between suspend functions and CompletableFuture - in both directions.
Your case is more tricky, because you need to implement an interface. Still, there are ways to handle this from Kotlin. We can for example create abstract implementation of LoginRepository in Kotlin. It would provide login(), but you would implement all remaining methods in Java. We can do a similar thing using composition instead of inheritance by creating a non-abstract implementation of LoginRepository in Kotlin (throwing errors from all unrelated functions) and delegating to it from the Java class. Or we can create a static function that performs the conversion from callback-based API to suspend API. This solution is the most flexible, but we need to mess with some coroutines internals from Java:
#file:JvmName("SuspendUtils")
// utility function in Kotlin, it converts callback API to a suspend function
suspend fun login(api: Api): LoginResponse = suspendCoroutine { cont ->
api.login { cont.resume(it) }
}
public static class LoginRepo implements LoginRepository {
private Api api = new Api();
#Nullable
#Override
public Object login(#NotNull Continuation<? super String> $completion) {
return SuspendUtils.login(api, $completion);
}
}
I am injecting a kotlin class into my java code. The kotlin class has two methods with nearly identical signatures:
fun <R> isFluxAuthorizedFor(
strings: List<StringRequest>,
then: (responses: List<StringResult>) -> Flux<R>
): Flux<R>
and
fun <R> isFluxAuthorizedFor(
string: StringRequest,
then: (per: StringResult) -> Flux<R>
): Flux<R> {
The kotlin class supports this overloading just fine.
However, I'm having a devil of a time getting my IDE to use the correct method. I have a method which matches the signature of the former:
private Flux<AuthorizedStrings> collectResults(List<StringResult> responses)
{
//not yet implemented
return null;
}
And yet, when I try and call the injected class' method, I get compilation errors:
List<StringRequest> allStrings = new ArrayList<StringRequest>();
Flux<UserReadAuthorizations> test = authCheck.isFluxAuthorizedFor(allStrings, (it) -> this.collectResults(it) );
The IDE makes two suggestions:
"change type of 'it' to 'List<StringResult>'"
"change method 'collectResults(List<StringResult>)' to 'collectResults(StringResult)'"
both of which imply that Java (or at least the compiler) can't figure out that I'm trying to call the other method. Is this a problem trying to integrate Java 8 and Kotlin? A quirk of the IDE? (I'm using Spring Tool Suite) Some silly user error that I've not yet been able to rubber-duck through?
I played with your code and found that IntelliJ tripped over the type of the lambda as a whole. I had to cast it -> this.collectResults(it) to the type Kotlin was expecting:
List<StringRequest> allStrings = new ArrayList<>();
Flux<UserReadAuthorizations> test = authCheck.isFluxAuthorizedFor(
allStrings,
(Function1<List<StringResult>, Flux<AuthorizedStrings>>) (it -> this.collectResults(it))
);
The signature of your methods themselves was not an issue. Hope this helps you in STS as well.
I'm confused on what the following line of code is supposed to be doing:
fun MyContext.req() = request as LaunchRequest
LaunchRequest is a Java class, and MyContext is a Kotlin data class. I've tried looking up examples of Kotlin code that use this syntax, but haven't found anything.
MyContext doesn't have a req() function, so is this just defining a new function for MyContext that returns a variable called "request" of type LaunchRequest?
It’s an extension function named req defined on the receiver MyContext. This technique is used to add new functions to existing classes without the use of inheritance. This concrete example req can be invoked on any object of MyContext.
If you have a reference of MyContext it may be used as follows:
val ctx: MyContext = ...
val req: LaunchRequest = ctx.req()
The as keyword is used to cast the variable request to LaunchRequest.
If the = in the function declaration also leads to confusion: it’s called function with expression body, which can be used to replace block bodies (enclosed in curly brackets) when the function contains a single expression like given in your code.
These are extension functions of Kotlin which help in improving a class's functionality without actually writing a lot of boilerplate code.The function could be also written as
fun MyContext.req(): LaunchRequest(){
return (request as LaunchRequest)
}
Another example of Extension function(to animate view)is:
fun View.animatePulsing() {
val animation = AnimatorSet()
....
....
animation.duration = 150
animation.start()
}
We can use this as:
txtView.animatePulsing()
I am very new to lambdas in Java.
I have started using it as i found them quite interesting
but i still don't know how to use them completely
I have a list of uuids and for each uuid i want to call a function which takes two parameters : first is a string and second is uuid
I am passing a constant string for each uuid
I have written a following code but its not working
uuids.stream()
.map(uuid -> {"string",uuid})
.forEach(AService::Amethod);
It is method which is another class AService
public void Amethod(String a, UUID b) {
System.out.println(a+b.toString());
}
A lambda expression has a single return value, so {"string",uuid} doesn't work.
You could return an array using .map(uuid -> new Object[]{"string",uuid}) but that won't be accepted by your AService::Amethod method reference.
In your example you can skip the map step:
uuids.stream()
.forEach(uuid -> aservice.Amethod("string",uuid));
where aservice is the instance of AService class on which you wish to execute the Amethod method.
uuids.stream().forEach(uuid -> AService.Amethod("string", uuid));
You can write something closer to your current code given a Pair class, but 1) you end up with more complicated code; 2) Java standard library doesn't have one built-in. Because of 2), there are quite a few utility libraries which define one. E.g. with https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html, it would be
uuids.stream()
.map(uuid -> Pair.of("string",uuid))
.forEach(pair -> AService.Amethod(pair.getLeft(), pair.getRight()));
I am trying to understand the ifPresent() method of the Optional API in Java 8.
I have simple logic:
Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));
But this results in a compilation error:
ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)
Of course I can do something like this:
if(user.isPresent())
{
doSomethingWithUser(user.get());
}
But this is exactly like a cluttered null check.
If I change the code into this:
user.ifPresent(new Consumer<User>() {
#Override public void accept(User user) {
doSomethingWithUser(user.get());
}
});
The code is getting dirtier, which makes me think of going back to the old null check.
Any ideas?
Optional<User>.ifPresent() takes a Consumer<? super User> as argument. You're passing it an expression whose type is void. So that doesn't compile.
A Consumer is intended to be implemented as a lambda expression:
Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));
Or even simpler, using a method reference:
Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);
This is basically the same thing as
Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
#Override
public void accept(User theUser) {
doSomethingWithUser(theUser);
}
});
The idea is that the doSomethingWithUser() method call will only be executed if the user is present. Your code executes the method call directly, and tries to pass its void result to ifPresent().
In addition to #JBNizet's answer, my general use case for ifPresent is to combine .isPresent() and .get():
Old way:
Optional opt = getIntOptional();
if(opt.isPresent()) {
Integer value = opt.get();
// do something with value
}
New way:
Optional opt = getIntOptional();
opt.ifPresent(value -> {
// do something with value
})
This, to me, is more intuitive.
Why write complicated code when you could make it simple?
Indeed, if you are absolutely going to use the Optional class, the most simple code is what you have already written ...
if (user.isPresent())
{
doSomethingWithUser(user.get());
}
This code has the advantages of being
readable
easy to debug (breakpoint)
not tricky
Just because Oracle has added the Optional class in Java 8 doesn't mean that this class must be used in all situation.
You can use method reference like this:
user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);
Method ifPresent() get Consumer object as a paremeter and (from JavaDoc): "If a value is present, invoke the specified consumer with the value." Value it is your variable user.
Or if this method doSomethingWithUser is in the User class and it is not static, you can use method reference like this:
user.ifPresent(this::doSomethingWithUser);
Use flatMap. If a value is present, flatMap returns a sequential Stream containing only that value, otherwise returns an empty Stream. So there is no need to use ifPresent() . Example:
list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());