Dagger and Kotlin - Issue with binding a class to its generic supertype - kotlin

I'm banging my head against the wall right now because I can't figure this out.
I have a generic Interface called Mapper which has two generic type parameters. Now I want to leverage multibinding and bind multiple implementations of this interface into a map of type Map<Class<out Any>, Provider<Mapper<Any, Any>>. My code looks as follows:
interface Mapper<DTO, Entity> {
fun toEntity(model: DTO): Entity
fun toDto(model: Entity): DTO
}
class PersistedIntakeEntryMapper #Inject constructor() : Mapper<PersistedIntakeEntry, IntakeEntry> {
override fun toEntity(model: PersistedIntakeEntry): IntakeEntry { TODO() }
override fun toDto(model: IntakeEntry): PersistedIntakeEntry { TODO() }
}
#Module
interface MapperModule {
#Binds
#IntoMap
#MapperKey(PersistedIntakeEntry::class)
#ModelMappers
fun bindPersistedIntakeEntryMapper(mapper: PersistedIntakeEntryMapper): Mapper<Any, Any>
}
#Singleton
class MapperFactory #Inject constructor(
#ModelMappers val mappers: Map<Class<out Any>, #JvmSuppressWildcards Provider<Mapper<Any, Any>>>,
) {
#Suppress("UNCHECKED_CAST")
inline fun <reified DTO: Any, Entity> get(): Mapper<DTO, Entity>? {
TODO()
}
}
Dagger is specifically complaining that PersistedIntakeEntryMapper is not assignable to Mapper<Any, Any>: MapperModule.java:13: error: #Binds methods' parameter type must be assignable to the return type.
However: the curious thing is that I have the same setup for another component which works like a charm:
interface ViewModelFactory<VM : ViewModel, SavedState, Parameters> {
fun create(savedState: SavedState?, parameters: Parameters?): VM
}
class SetCalorieGoalViewModelFactory #Inject constructor(
private val getCalorieGoalUseCase: GetCalorieGoalUseCase,
private val setCalorieGoalUseCase: SetCalorieGoalUseCase,
private val navigator: Navigator,
) : ViewModelFactory<SetCalorieGoalViewModel, SetCalorieGoalUiState, Nothing> {
override fun create(savedState: SetCalorieGoalUiState?, parameters: Nothing?): SetCalorieGoalViewModel {
TODO()
}
}
#Module
interface SetCalorieGoalUiModule {
#Binds
#IntoMap
#ViewModelKey(SetCalorieGoalViewModel::class)
fun bindSetCalorieGoalViewModelFactory(factory: SetCalorieGoalViewModelFactory)
: ViewModelFactory<ViewModel, Any, Any>
}
I can bind the SetCalorieGoalViewModelFactory to the ViewModelFactory<SetCalorieGoalViewModel, Any, Any> type without issue. What is the difference between these setups that makes one of them work and the other one not? I can't figure it out for the life of me. Big thanks in advance to anyone trying to solve this problem.

First of all, check out kotlin documentation on the generic variance topic as well as the related java topics (since dagger generates java code).
Generally the issue is that Mapper<PersistedIntakeEntry, IntakeEntry> and Mapper<Any, Any> are invariant, meaning that one is not subtype of the other. Basically this assignment val mapper: Mapper<Any, Any> = PersistedIntakeEntryMapper() will not compile and that's what dagger tells you. And that makes sense, since Mapper<Any, Any> must be able to map Any to Any and that's obviously not the case with PersistedIntakeEntryMapper - it expects PersistedIntakeEntry and IntakeEntry.
Following the documentation above, it would be possible if your declaration had out modifier specified like interface Mapper<out DTO, out Entity>, but that will not work in your case, since you have your type arguments in in positions.
The interesting question is why it works with ViewModelFactory. It seems to be a bug in KAPT, it just omits generic type parameters in the generated code when it sees Nothing. It makes it bypass the compiler checks (but it does not make it safe to use at runtime!), since generics are mostly compile-time things (see type erasure in java).

Related

Can I omit type in generics? - Kotlin

If I have a following interface:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
Would it be possible have implementation of this interface that does not take Params?
To have effectively something like:
interface BaseDataRemote<T> {
fun getData(): Single<T>
}
Implementation is as follows:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
I use Dagger 2 to module to bind this implementation:
#Module
internal interface RemoteModule {
#Binds
#CoreScope
fun bindsSellerRemote(remoteSellerDataSource: RemoteSellerDataSource): BaseDataRemote<SellerEntity, Nothing>
}
I tried using Nothing as second type parameter, but it does not seem to work
(I'm getting required: class or interface without bounds error
Full error message:
RemoteSellerDataSource.java:6: error: unexpected type
public final class RemoteSellerDataSource implements com.bigchangedev.stamps.business.sdk.data.base.data.BaseDataRemote<SellerEntity, ?> {
^
required: class or interface without bounds
found:?
Thanks.
EDIT: the original answer was a pure Kotlin answer because the OP didn't mention Dagger.
Using Nothing is correct and works in pure Kotlin. However, Dagger seems to convert your code to Java, and in doing so it uses wildcards for the generics (which it doesn't like because it wants exact type matches). To avoid this issue, you can try using #JvmSuppressWildcards on your generic type parameters:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, #JvmSuppressWildcards Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
Although I'm not sure what will happen in Java with Nothing in that case. I guess this should have the same effect on the Java code as removing the in variance for the second type param in the interface declaration, but without weakening your Kotlin types.
Another workaround would be to use Unit instead of Nothing, which Dagger will most likely convert to Void in this case. This is not great for your types, though.
Original answer:
You can technically already call getData() without arguments thanks to the default value. An implementation that doesn't care about the params argument can simply expect null all the time.
The Kotlin type that only contains null and no other value is technically Nothing?, and since getData is defined with Params? (note the ?) as input, it should be correct to specify Nothing (even without ?) as second type argument. So you should be able to define an implementation like this:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> {
// params will always be null here
}
}
To avoid confusion for the users, this implementation may additionally provide a getData() method without arguments at all:
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> = getData()
fun getData(): Single<T> {
TODO("implementation")
}
}

Kotlin class generics without duplication

Consider an abstract class:
abstract class PubSubSubscriber<T : Any>(private val topic: KClass<T>) : BackgroundFunction<PubSubMessage> {
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
}
And implementation:
class MySubscriber : PubSubSubscriber<Payload>(Payload::class) {
Is there a way to define such abstract class so that I don't have to repeat twice the Payload and Payload::class in the class definition?
Yes, with some reflection.
At construction time, we can extract the type parameter and assign it to a property that no longer needs to be given to the constructor:
abstract class PubSubSubscriber<T : Any> {
val topic: KClass<T> = extractTypeParam<T>(0).kotlin
private fun <X> extractTypeParam(paramIdx: Int): Class<X> {
require(PubSubSubscriber::class.java == javaClass.superclass) {
"PubSubSubscriber subclass $javaClass should directly extend PubSubSubscriber"
}
#Suppress("UNCHECKED_CAST")
return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[paramIdx] as Class<X>
}
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
Note the following limitations:
A) this solution works only if MySubscriber directly extends from PubSubSubscriber. However, the given code can detect if that's not the case and warn about it (at runtime). In such cases, there are the following solutions:
MySubscriber falls back to providing a duplicate argument (essentially what you already had)
the direct superclass of MySubscriber can provide a similar detection mechanism
B) You call reflection code every time a MySubscriber instance is created. This may be too slow in certain contexts, but for many this is unproblematic.

Implementing multiple overloaded interfaces with `by`

I'd like to leverage by to build class APIs in a nice way. Is there any way to do something like the following?
interface Foo<T> {
fun foo(t: T)
}
inline fun <reified T> createFoo() = object : Foo<T> {
override fun foo(t: T) {
// do stuff
}
}
// This is an error
class StringIntFoo : Foo<String> by createFoo(), Foo<Int> by createFoo()
fun main(){
val foo = StringIntFoo()
foo.foo("")
foo.foo(2)
}
// Doing it manually obviously isn't an issue
class ManualStringIntFoo {
fun foo(t: String){
}
fun foo(t: Int){
}
}
Link to a playground.
It looks like the generated method end up having the same JVM signature. I was hoping the reified types would get around it. With only a single implementation it works just fine and the types look correct.
Is there some way of actually doing this? Whether or not the StringIntFoo is technically a Foo I suppose isn't important for the problem at hand. It would be cool to be able to construct classes in this way.
Doing it manually doesn't work either if you try to actually implement the interfaces: ManualStringIntFoo : Foo<String>, Foo<Int> gives the same error as StringIntFoo.
So by can't help because it still compiles to class StringIntFoo : Foo<String>, Foo<Int> only setting the implementation of methods.

Parcelable overload resolution ambiguity

I am trying to create a POJO (aka data classes in Kotlin) structure of a JSON response in Kotlin. I've implemented the Parcelable interface for each data class in the structure. In all of the data classes, I've auto generated the Parcelable implementation. The issue is the generated second constructor where the IDE is complaining about:
Overload resolution ambiguity
It states that it's being confused between these two constructors:
public constructor GeocodeRes(parcel: Parcel)
public constructor GeocodeRes(responset: ResponseRes)
Which I believe makes sense because ResponseRes is also of type Parcelable (ResponseRes implements Parcelable). So calling the GeocodeRes(parcel) method (within the createFromParcel companion method), it is getting confused.
That was until I removed ResponseRes from implementing the Parcelable class and it's still showing the same error.
Is there any reason to this? Am I setting this up properly? In all of the children data classes, they all implement the Parcelable interface (with dependence with eachother) but aren't running into any issues.
Here's my GeocodeRes class:
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
data class GeocodeRes(
#SerializedName("Response") #Expose val responset: ResponseRes
) : Parcelable {
// this is the problem. the IDE is complaining that the usage is too ambiguous (). however, the only usage of this constructor is within this class - just doesn't tell me where exactly.
constructor(parcel: Parcel) : this(parcel.readParcelable(ResponseRes::class.java.classLoader)) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(responset, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GeocodeRes> {
override fun createFromParcel(parcel: Parcel): GeocodeRes {
return GeocodeRes(parcel)
}
override fun newArray(size: Int): Array<GeocodeRes?> {
return arrayOfNulls(size)
}
}
}
Here's my ResponseRes class:
data class ResponseRes(
#SerializedName("MetaInfo") #Expose val metaInfo: MetaInfo,
#SerializedName("View") #Expose val views: List<View>
): Parcelable
{
[...]//parcel methods
}
however, the only usage of this constructor is within this class - just doesn't tell me where exactly
The problem is with the definition itself, not with any usage. It could never be used, and the error would still be there.
You should be able to fix this by specifying which Parcelable you want to read:
this(parcel.readParcelable<ResponseRes>(ResponseRes::class.java.classLoader))
The compiler can't decide if you mean that or
this(parcel.readParcelable<Parcel>(ResponseRes::class.java.classLoader))
Even though the second wouldn't be legal because Parcel doesn't implement Parcelable, if you look at the signature
<T extends Parcelable> T readParcelable(ClassLoader loader)
you can see only the return type can be used to infer T, not the argument. So the compiler need to pick the constructor overload before trying to infer T.

How do I call a method in Kotlin with a different upper bound?

e.g. Given a Class<T> how do I call/invoke a method/constructor that requires Class<T> where T : Enum<T>?
fun <T : Any> handleAny(classOfT: Class<T>) {
if (classOfT.isEnum) {
handleEnum(classOfT)
}
}
fun <T : Enum<T>> handleEnum(classOfT: Class<T>) { /*...*/ }
Error: inferred type T is not a subtype of kotlin.Enum<T>
In Java I can do an unchecked call but I cannot seem to find a way to do anything similar in Kotlin.
As for now I found this quite hacky workaround for it:
private enum class DummyEnum
fun <T> handleAny(classOfT: Class<T>) {
if (classOfT.isEnum) {
handleEnum(classOfT as Class<DummyEnum>) //absolutely any enum goes
}
}
fun <T : Enum<T>> handleEnum(classOfT: Class<T>) {
println(classOfT.toString())
}
The idea is to make an unchecked cast to the type with any generic parameter satisfying the upper bound (let it be DummyEnum), which will then be erased at runtime anyway.
The limitation is that the solution doesn't work correctly with reified generics: if handleEnum had reified type parameter, it would be substituted for statically inferred type (DummyEnum).