1

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.

Joffrey
  • 32,348
  • 6
  • 68
  • 100
leszekt80
  • 15
  • 3
  • `Nothing` should be the way to go, can you show the code that gives you the error you mention? – Joffrey Aug 09 '21 at 22:15
  • Is `RemoteSellerDataSource` defined in a Java file? Why does the error mention `RemoteSellerDataSource.java`? – Joffrey Aug 09 '21 at 22:39
  • It defined in Kotlin, but it gets converted to java, because Dagger 2 works with java files – leszekt80 Aug 09 '21 at 22:44
  • I wonder if this happens because there is no 'Nothnig' type in java – leszekt80 Aug 09 '21 at 22:48
  • It might be a limitation of Dagger then. In that case, a 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. You can also try `@JvmSuppressWildcards` on your generic type parameters. – Joffrey Aug 10 '21 at 08:14

1 Answers1

1

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")
    }
}
Joffrey
  • 32,348
  • 6
  • 68
  • 100
  • Thanks @Joffrey. One thing that might be important is that I use Dagger to bind implementation. I added implementation in description... – leszekt80 Aug 09 '21 at 22:21
  • I'm not entirely sure why it matters. What exactly fails in your case? Maybe you can update the question with additional information about how you use Dagger to instantiate this, and where/when you get errors – Joffrey Aug 09 '21 at 22:27
  • Thanks @Joffrey I added more details in description – leszekt80 Aug 09 '21 at 22:35
  • Thanks @Joffrey. `@JvmSuppressWildcards` did now work for me, but Unit worked perfectly. I appreciate your help. Thank you. – leszekt80 Aug 10 '21 at 18:21