How to Override a suspend function in java class - java

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);
}
}

Related

Can you scope a Kotlin interface for use with only certain classes?

I come from primarily an iOS background. In Swift, we can make Protocols which are designed to be implemented by specific classes:
protocol MyViewControllerProtocol where Self: UIViewController {
func doViewControllerThings()
}
This is super powerful because it allows us to extend the protocol with a default implementation and use methods on self
extension MyViewControllerProtocol {
func doViewControllerThings() {
self.view.addSubview(UIView())
}
}
I am trying to do something similar in Kotlin for an Android project. Is this possible?
Not strictly. You could add a generic property that is intended for the implementer to return itself, but the compiler won't prevent you from providing some other object to satisfy the property.
interface SomeInterface {
val thisRef: Activity
// ...
}
The above interface can call functions on thisRef in its default function implementations.
An implementing class must provide the value for thisRef:
class MyActivity: Activity(), SomeInterface {
override val thisRef = this
//...
}
I don't think you can restrict interfaces in this way, but you can restrict functions:
interface MyViewController
fun <T> T.doViewControllerThings() where T : UIViewController, T : MyViewController {
view.addSubview(UIView())
}

Why can't Java infer the type of a lambda expression with a generic type parameter?

Given the following code:
abstract class Event {
}
class MyEvent extends Event {
}
interface EventSubscriber<T extends Event> {
void onMessage(T message);
Class<T> getMessageType();
}
interface MyEventSubscriber extends EventSubscriber<MyEvent> {
#Override
default Class<MyEvent> getMessageType() {
return MyEvent.class;
}
}
class SubscriberManager {
public void subscribe(EventSubscriber<? extends Event> subscriber) {
}
}
I would like to get access to the generic type parameter which an event subscriber holds via calling the getMessageType method.
I also would like to use the SubscriberManager by passing the lambda expressions to the subscribe method:
subscriberManager.subscribe((MyEvent event) -> {});
Unfortunately, the Java compiler can't infer the type of the lambda expression which is passed to the subscribe method, although it's pretty obvious to me that the type of the lambda could be deduced from the lambda's argument - MyEvent -> MyEventSubscriber. The Java compiler gives me the following error:
incompatible types: EventSubscriber is not a functional interface
multiple non-overriding abstract methods found in interface EventSubscriber
So I need to specify the type of the lambda expression or use the anonymous class in order to bypass this limitation:
MyEventSubscriber myEventSubscriber = (MyEvent event) -> {};
subscriberManager.subscribe(myEventSubscriber);
subscriberManager.subscribe(new MyEventSubscriber() {
#Override
public void onMessage(MyEvent message) {
}
});
I could add an overloaded method to the SubscriberManager class, remove the getMessageType method from the EventSubscriber interface (since we would know the actual type of the subscriber and therefore the message type it holds) and use the simple lambda expression I mentioned in the first code example, but it would make the whole code less 'polymorphic` I guess:
class SubscriberManager {
public void subscribe(EventSubscriber<? extends Event> subscriber) {
}
public void subscribe(MyEventSubscriber subscriber) {
}
}
The problem is that your EventSubscriber interface isn't a functional interface, as the error tells you: There are 2 methods to implement there. The fact that you've made an actual functional interface named MyEventSubscriber would mean that you want java to somehow intuit that this exists.
Java is not in the business of scouring the millions of classes in the classpath just to look and see if there's anything in that entire mess that may or may not work. I hope by putting it that way it's obvious why that is and why it'll never work that way.
Specifically: Because there's a lambda, java needs to target type it. To do this, java first checks the context surrounding the lambda: It checks the various methods named subscribe and notices there's only one, and it needs an argument of type EventSubscriber. It then target-types the lambda to this type, and immediately fails, as it is not a functional interface. There is no way for the compiler to figure it out it should be target-typing to MyEventSubscriber.
I did some toying around with using reflection to check the actual type, but that does not work; you'll have to find another way to tackle this problem.

Call Kotlin object with class delegation from Java as a static method

This may be a bit difficult to describe, so I'll try to give a concrete example of what I'm trying to do.
Suppose we have a Facade interface and class (in Java), like this:
interface FacadeInterface<T> {
void method(String from, String via);
}
class Facade<T> implements FacadeInterface<T> {
private Class<T> mClazz;
public Facade(Class<T> clazz) {
mClazz = clazz;
}
#Override
public void method(String from, String via) {
System.out.println("Method called from " + from + " via " + via);
}
}
In my applications, I need to have multiple singletons which hold an instance of the facade. The real facade has additional setup/config parameters but those are irrelevant here.
Before I started using kotlin, I would have a class which holds a static instance of the facade (not really a singleton, but in my case, it served a similar purpose) which proxied the calls to the facade, like this:
public class Singleton {
private static final FacadeInterface<String> sFacade = new Facade<>(String.class);
private Singleton() {
}
public static void method(String from, String via) {
sFacade.method(from, via);
}
}
Now, with Kotlin we have class delegates which allow me to write something like this:
object SingletonKt : FacadeInterface<String> by Facade(String::class.java)
This is great - no more boilerplate and I can call SingletonKt from Kotlin classes the same way I called the java Singleton:
Singleton.method("Kotlin", "Singleton")
SingletonKt.method("Kotlin", "SingletonKt")
But, a slight problem arises when I use SingletonKt from Java. Then I have to specify INSTANCE:
Singleton.method("Java", "Singleton");
SingletonKt.INSTANCE.method("Java", "SingletonKt");
I am aware of the #JvmStatic annotation, but the only place I can put it in the SingletonKt file without causing compile errors is right before FacadeInterface and it doesn't seem to do the trick.
Is there a way to set up this class delegate so that I can call it from Java as if it were a static method, without introducing the boilerplate of creating proxy methods for SingletonKt (which would defeat the purpose of the class delegate)?
It's sadly not possilble!
The Kotlin Delegation is a nice way to reduce boilerplate code. But it comes with the inability to actually access the delegate within the class body.
The second issue you're facing regarding #JvmStatic is actually more drastic to your cause than the first and also applies to you when implementing the delegation manually:
Override members cannot be '#JvmStatic' in object
So instead of exposing the method() through the INSTANCE only, you could delegate it to a staticMethod() on the object. This still differs from your intent, but comes close to it.
object SingletonKt : FacadeInterface<String> by Facade(String::class.java)
#JvmStatic fun staticMethod(from: String, via: String) = method(from, to)
}
I don't know if it is possible to have delegated methods as static methods inside an object in Kotlin.
However, as you are interested in creating singletons that proxy a class, you could use package-level constants in Kotlin:
val SingletonKt : FacadeInterface<String> = Facade(String::class.java)
Now, you can call SingletonKt.method just like you would in Java. Note that you need to use a static import in Java to be able to use the SingletonKt constant.
This also allows you to use features like lazy to only create the singleton (or, in this case, instance) when you need it.

Subscribe to an Observable without triggering it and then passing it on

This could get a little bit complicated and I'm not that experienced with Observables and the RX pattern so bear with me:
Suppose you've got some arbitrary SDK method which returns an Observable. You consume the method from a class which is - among other things - responsible for retrieving data and, while doing so, does some caching, so let's call it DataProvider. Then you've got another class which wants to access the data provided by DataProvider. Let's call it Consumer for now. So there we've got our setup.
Side note for all the pattern friends out there: I'm aware that this is not MVP, it's just an example for an analogous, but much more complex problem I'm facing in my application.
That being said, in Kotlin-like pseudo code the described situation would look like this:
class Consumer(val provider: DataProvider) {
fun logic() {
provider.getData().subscribe(...)
}
}
class DataProvider(val sdk: SDK) {
fun getData(): Consumer {
val observable = sdk.getData()
observable.subscribe(/*cache data as it passes through*/)
return observable
}
}
class SDK {
fun getData(): Observable {
return fetchDataFromNetwork()
}
}
The problem is, that upon calling sdk.subscribe() in the DataProvider I'm already triggering the Observable's subscribe() method which I don't want. I want the DataProvider to just silently listen - in this example the triggering should be done by the Consumer.
So what's the best RX compatible solution for this problem? The one outlined in the pseudo code above definitely isn't for various reasons one of which is the premature triggering of the network request before the Consumer has subscribed to the Observable. I've experimented with publish().autoComplete(2) before calling subscribe() in the DataProvider, but that doesn't seem to be the canonical way to do this kind of things. It just feels hacky.
Edit: Through SO's excellent "related" feature I've just stumbled across another question pointing in a different direction, but having a solution which could also be applicable here namely flatMap(). I knew that one before, but never actually had to use it. Seems like a viable way to me - what's your opinion regarding that?
If the caching step is not supposed to modify events in the chain, the doOnNext() operator can be used:
class DataProvider(val sdk: SDK) {
fun getData(): Observable<*> = sdk.getData().doOnNext(/*cache data as it passes through*/)
}
Yes, flatMap could be a solution. Moreover you could split your stream into chain of small Observables:
public class DataProvider {
private Api api;
private Parser parser;
private Cache cache;
public Observable<List<User>> getUsers() {
return api.getUsersFromNetwork()
.flatMap(parser::parseUsers)
.map(cache::cacheUsers);
}
}
public class Api {
public Observable<Response> getUsersFromNetwork() {
//makes https request or whatever
}
}
public class Parser {
public Observable<List<User>> parseUsers(Response response) {
//parse users
}
}
public class Cache {
public List<User> cacheUsers(List<User> users) {
//cache users
}
}
It's easy to test, maintain and replace implementations(with usage of interfaces). Also you could easily insert additional step into your stream(for instance log/convert/change data which you receive from server).
The other quite convenient operator is map. Basically instead of Observable<Data> it returns just Data. It could make your code even simpler.

Confusion with Kotlin generics

I'm new to Kotlin and I'm trying to write code which does something fairly simple, however I cannot figure out how to use generics to get it to work.
I have a Handler trait which represents a handler for things. I cannot change the code for a Handler as it comes from a library.
trait Handler<T> {
fun handle(result: T)
}
All of the code below is in my control -
User is an open class that has subclasses such as AdminUser and GuestUser etc.
A trait called AdminUserAction does something to create a List of AdminUsers and then passes the list to a handler for List<AdminUser> -
trait AdminUserAction {
fun then(handler: Handler<List<AdminUser>>)
}
Now I want to pass an AdminUserAction a handler for User instead of AdminUser. Let's say the handler simply logs the names of the users, and doesn't do anything with Admin specific properties.
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames)
}
However, this code gives me a TypeMismatch.
Since the Handler is of the type List<T> and is immutable, the preceding code should be completely safe, however the compiler isn't able to figure it out.
If I had access to the code for Handler I could do the following and it would work -
trait Handler<in T> {
fun handle(result: T)
}
However, as I said before, I cannot modify Handler as it comes from a library. Also, it seems hacky to have to do this because the type of Handler is fully general and should be usable for other kinds of handlers too.
I tried subclassing Handler and using that -
trait ListHandler<in T>: Handler<List<T>> { }
However now I get an error that says "Parameter T is declared as 'in' but occurs in 'invariant' position in Handler>"
I tried -
trait ListHandler<in T>: Handler<List<in T>> { }
But that gives me more errors.
Why is this so confusing? And how can I use generics to get the preceding code to work?
Edit:
I can make it work by writing a generic function that converts a Handler<List<User>> into Handler<List<AdminUser>> -
fun <T: User> fromGeneric(handler: Handler<User>): Handler<T> {
return object: Handler<T> {
override fun handle(result: List<T>) {
handler.handle(result)
}
}
}
And then -
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(fromGeneric(printAllNames))
}
But, this seems so wasteful. Especially look at the body of the conversion function fromGeneric. It is doing nothing! Yet I have to go through the rigamarole of using it everytime just to satisfy the types.
Is there a better way? Is it technically possible to make the Kotlin compiler smarter so that this type jugglery is not needed?
There are a couple of solutions:
Change the definition of AdminUserAction to
trait AdminUserAction {
fun then(handler: Handler<in List<AdminUser>>)
}
or change the definition of AdminUserAction to
trait AdminUserAction {
fun then(handler: Handler<List<User>>)
}
or just cast printAllNames like this
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames as Handler<List<AdminUser>>)
}

Categories