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 DogProcessor
s:
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.