Open CanOrhan opened 5 years ago
I actually kinda do https://github.com/Zhuinden/guide-to-kotlin/wiki/2.)-Basic-Kotlin-Features#generics-t-blah-inout-and-star-projection-
But it's not that fleshed out because I use star projection when I can't find another way to do it. 🤔
Also I can't wrap my head around in
/out
and add them only when the compiler is yelling at me.
So in that sense, I do accept explanations that do not involve the term "covariance" and "contravariance" because nobody really understands those terms (and that is where all confusion comes from).
Ahh sorry! I'd missed that bit. To be honest, I'm on the same boat. I'm not super confident with when to use in/out but there is one part of the official documentation that did help - The two example just before star-projections.
Short snippet: You can project a type with in as well:
fun fill(dest: Array<in String>, value: String) { ... }
Array<in String>
corresponds to Java's Array<? super String>
, i.e. you can pass an array of CharSequence
or an array of Object
to the fill()
function.
Perhaps it'd be worth including those examples as they're very Java-centric?
I get confused because you need to use @JvmSuppressWildcards
to prevent automatically adding ? extends ViewModel
, and I'm not sure why that happens.
I'm at working reading through https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-suppress-wildcards/index.html. I think it might only apply to out
? Luckily, the xmas holiday is coming :D
@Zhuinden @CanOrhan
The first step is to understand why variance is important in the first place.
For example this java snippet that use Arrays (who have no variances) is unsafe:
void replace(Number[] numbers) {
numbers[0] = 1.0f;
}
void test() {
Integer[] array = new Integer[10];
replace(array);
// Our Integer[] array now contains a float!!
}
Take-away: Variance is really important to pass generics to other functions and when you work with collections of generics.
Now to undertand in
and out
in Kotlin, I ask myself whether the my generic class is a Producer
or a Consumer
or if it's both.
A producer-only (like List
) looks like this:
class Producer<out T >(
val beverage: T
){
fun produce() : T {
return beverage
}
}
A consumer-only class (like Comparable
) look like this
class Consumer<in T> {
fun consume(t: T) {
println("Drinking $t!")
}
}
Some class like MutableList
are both a Producer and a Consumer
class ProducerAndConsumer<T: Any> {
lateinit var value: T
fun updateValue(newValue: T) {
value = newValue
}
fun useValue() {
println(value)
}
}
MutableList
is an example that is both a Producer and a Consumer. In this case, the compiler won't let you assign a MutableList<A>
to a MutableList<B>
no matter what, for your own safety.
A complete example here
class Producer<out T : Beverage>(
val beverage: T
){
fun produce() : T {
return beverage
}
}
class Consumer<in T: Beverage> {
fun consume(t: T) {
println("Drinking $t!")
}
}
class BadProducer<T: Beverage>(
val beverage: T
) {
fun produce() : T {
return beverage
}
}
class BadConsumer<T: Beverage> {
fun consume(t: T) {
println("Drinking $t!")
}
}
interface Beverage
interface Alcohol: Beverage
object Coffee : Beverage
object Vodka: Alcohol
object Whisky: Alcohol
fun main(args: Array<String>) {
// OUT Generics
val colombia: Producer<Coffee> = Producer(Coffee)
val producer: Producer<Beverage> = colombia
// real-world example
val drinkWithModeration: List<Alcohol> = listOf(Whisky, Vodka)
val alcohols : List<Beverage> = drinkWithModeration
// without OUT Generics
val someOtherCountry : BadProducer<Coffee> = BadProducer(Coffee)
val badProducer: BadProducer<Beverage> = someOtherCountry // Compile ERROR: Type Mismatch
// real-world example
val drinkWithModeration2: MutableList<Alcohol> = mutableListOf(Whisky, Vodka)
val alcohols2 : MutableList<Beverage> = drinkWithModeration2 // Compile ERROR: Type Mismatch
// IN Generics
val someone = Consumer<Beverage>()
val me: Consumer<Coffee> = someone
// without IN Generics
val someFriend = BadConsumer<Beverage>()
val peter: BadConsumer<Vodka> = someFriend // Compile ERROR: Type Mismatch
}
Do you think it'd be a good idea to have a section on generics in Kotlin?