Home > Enterprise >  Kotlin Generic auto conversion to "out"
Kotlin Generic auto conversion to "out"

Time:06-03

In the below code if we use generic in base and then extend it in a diff interface, kotlin doesn't respect the generic of the base interface.

Why is that so?

In the base I have not used "in" or "out" but still the extended interface by default becomes "out".

interface FeaturedCardAdapterContract {
interface View {
    fun onCreate()
  }

interface SubPresenter<V : View> {
    fun onBind(v: V)
  }
}

interface FeaturedTestAdapterContract {
   interface View : FeaturedCardAdapterContract.View
   interface Presenter : FeaturedCardAdapterContract.SubPresenter<View>
}

fun main() {
val featureImpl1: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
    override fun onBind(v: FeaturedTestAdapterContract.View) {
    }
}
val featureImpl2: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
    override fun onBind(v: FeaturedTestAdapterContract.View) {
    }
}

//Works but i won't be able to consume it in onBind bcz kotlin assumed it as "out"
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)

//Dosen't Work-bcz kotlin assumes the type of featureImpl1 is FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View> ,Why?
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)

//Works but,Same as 1st method
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<*>> = arrayOf(featureImpl1, featureImpl2)

for (featureImpl in interfaceArray) {
    //Won't work bcz of "out"
    featureImpl.onBind(object : FeaturedCardAdapterContract.View {
        override fun onCreate() {
            //
        }
    })
}
}

CodePudding user response:

Rename the interfaces to Processor, Animal, and Dog, and you will see why the compiler is correct about the types and what you are trying to do doesn't make sense.

Here's the renaming:

interface Animal // FeaturedCardAdapterContract.View

interface Processor<A: Animal> { // FeaturedCardAdapterContract.SubPresenter<V>
    fun process(animal: A) // onBind
}

interface Dog: Animal // FeaturedTestAdapterContract.View
interface DogProcessor: Processor<Dog> // FeaturedTestAdapterContract.Presenter

In main, you are creating an array of 2 DogProcessors:

val processorImpl1 = object: DogProcessor {
    override fun process(animal: Dog) {

    }
}

val processorImpl2 = object: DogProcessor {
    override fun process(animal: Dog) {

    }
}

val array = arrayOf(processorImpl1, processorImpl2)

Then you are trying to loop through it and have them each process an animal:

val array = arrayOf(processorImpl1, processorImpl2)
for (processor in array) {
    processor.process(object: Animal {

    })
}

This is obviously not going to work no matter how you change the type of array. The processors in the array process dogs specifically, not animals in general. You could simply make this work by just giving it dogs instead of animals, or in your case:

val interfaceArray = arrayOf(featureImpl1, featureImpl2)

for (featureImpl in interfaceArray) {
    featureImpl.onBind(object : FeaturedTestAdapterContract.View {
        override fun onCreate() {
            //
        }
    })
}

Note that the type of the array can be changed to Array<Processor<out Animal>> - an array of processors that only produces animals. This is because a producer of dogs is a kind of producer of animals. See also: PECS. However, since you want to call process (onBind) here, you want the processor to take in, or consume an animal, not produce one. Therefore, Array<Processor<out Animal>> is not what you want.

CodePudding user response:

Just to clarify, you have defined featureImpl1 as FeaturedTestAdapterContract.Presenter, so it's a FeaturedCardAdapterContract.SubPresenter<FeaturedTestAdapterContract.View>.

Note the "Test" view here, not the "Card" one. This is your own definition of Presenter - the View you use in the definition is a shortcut for the test view FeaturedTestAdapterContract.View, NOT the card one FeaturedCardAdapterContract.View:

val featureImpl1: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
    // only wants test views here
    override fun onBind(v: FeaturedTestAdapterContract.View) {
}

Now check this part:

Won't work bcz of "out"

featureImpl.onBind(object : FeaturedCardAdapterContract.View {
    //...
})

Let's forget about out for the moment. You have defined your featureImpl1 so it accepts to bind only to the specific FeaturedTestAdapterContract.View. But here you're trying to pass a card view FeaturedCardAdapterContract.View, which is NOT a test view. If this were allowed, the body of featureImpl1 would just fail because it is given objects that are NOT of type FeaturedTestAdapterContract.View, nor even subtypes of it.

//Works but i won't be able to consume it in onBind bcz kotlin assumed it as "out"
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)

Kotlin didn't assume anything here, you're marking out yourself. But it's normal that you have to write it because of what I explained above.

We've just seen that featureImpl1 is a SubPresenter<FeaturedTestAdapterContract.View>. It cannot be assigned to a SubPresenter<FeaturedCardAdapterContract.View> (without out) because that would mean it would need to accept more types than it actually can.

  • Related