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>>)
}
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 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())
}
I have a scenario where I found to build a hierarchy of type as follows:
IComponent (interface) -> AComponent (class)
Here I want IComponent to contain a method exeucte() which would be implemented by all the classes which implement this interface. Now, apart from the component hierarchy I also want a request/response format for all the components so I thought of below things:
IComponentRequest -> AComponentRequest
IComponentResponse -> IComponentResponse
Now I want to declare the execute() method in IComponent in such a way that all the classes which want to implement can follow the request and response classes of the above mentioned request/response hierarchy. Explaining in terms of code, I am currently declaring the method as below:
public interface IComponent<IComponentRequest,IComponentResponse> {
IComponentResponse execute(IComponentRequest request);
}
What I actually want is something like :
<Any type which implements IComponentResponse> execute(<Any type which implements IComponentRequest> request) {
}
I tried using generics like :
public interface IComponent<REQUEST,RESPONSE> {}
But in these cases I face issue in implementations while calling methods and receiving responses that REQUEST and RESPONSE are not any particular java types. Hence, I again went on to use the proper interfaces IComponentRequest and IComponentResponse but there I am not able to apply bounded generics.
Can someone please help on what am I missing here ?
When you specify the generic name, you can also specify type information. Here, you want extends for each appropriate type. Something like
public interface IComponent<REQUEST extends IComponentRequest,
RESPONSE extends IComponentResponse> {
RESPONSE execute(REQUEST request);
}
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.
I have got the following interface which is implemented by the following class. For this class, I would like to be able to use method chaining, which is why I added a "return this" at the end of the addFilter() method:
public interface IFilteredDataService<B extends Bean> extends IDataService<B>
{
FilteredDataService applyFilter(Predicate<B> filter);
}
public class FilteredDataService<B extends Bean> implements IFilteredDataService<B>
{
#Override
public FilteredDataService addFilter(Predicate<B> filter)
{
filters.add(filter);
return this;
}
}
When I use the addFilter() method in the following way, everything is fine:
someInstance.addFilter(foo);
someInstance.addFilter(bar);
When I use the method chaining like this:
someInstance.addFilter(foo).addFilter(bar);
it still works fine, but I get the following warning:
Unchecked call to 'addFilter(Predicate<B>)' as a member of raw type 'FilteredDataService'.
I was not able to figure out why this happens and how to remove it. Any help would be greatly appreciated.
You're missing the generic information in your return value, returning a raw (non-generic) FilteredDataService.
Use public FilteredDataService<B> addFilter(Predicate<B> filter) to keep the generics.
When you want to use generic and chain this method, you have to create fully generic method (include returning) as Kayaman wrote. And take a look at what you getting:
Unchecked call to 'addFilter(Predicate)' as a member of raw type 'FilteredDataService'.
In simple translation: You are calling method which should accept generic param (addFilter(Predicate filter)) but you are sending nongeneric param during chaining (return this; = FilteredDataService -> Non-Generic).
Just for clarify :o)