Like Java generics, Kotlin’s generics are used to restrict their types

class Test<T: CallBack> {
   fun add(t: T){
      t.run()
      t.callback()
   }
}

// Use the WHERE keyword if T inherits more than one parent
// where T:CallBack, T:Runnable indicates that T is a subclass of CallBack and Runnable
class Test<T> where T:CallBack.T:Runnable{
   fun add(t: T){
      t.run()
      t.callback()
   }
}
Copy the code

Reified generics

Access to a generic type is not supported by default. Use the reified keyword to support access to the type of a generic. Also, a function with reified true generic must be inline, which makes sense because inline copies the code of the function to the place where it is called to know the type of the specific generic.

// functions with reified must be inline
inline fun <reified T : Activity> Activity.start(a) {
    // T::class. Java T is true generics to access JVM class objects
    startActivity(Intent(this, T::class.java));
}
Copy the code
inline fun <reified T> printIfTypeMatch(item: Any) {
    if (item is T) { // 👈 there will be no errors
        println(item)
    }
}
Copy the code

Covariant OUT and contravariant IN

Kotlin’s covariant and contravariant works the same way as Java. The only difference is using different keywords.

  • Koltin uses the keyword out to support covariation, equivalent to Java’s upper bound wildcard? Extends includes both A and subclasses of A

  • Kotlin uses the keyword in to support contravariant, equivalent to Java’s lower bound wildcard? Super includes the parent classes of A and A

Covariant OUT represents that out A is A subclass of its type A. Its type is uncertain and cannot be modified. Only base class A can be read.

Fission in goes the other way, in A means it’s A parent of A. Base classes are indeterminate and do not support GET. But A must be A subtype of this unknown type, which can be modified based on polymorphism

// out
class Producer<T> {
    fun produce(a): T? {
        return null}}val producer: Producer<out TextView> = Producer<Button>()
val textView: TextView? = producer.produce()

// in
class Consumer<T> {
    fun consume(t: T){}}val consumer: Consumer<in Button> = Consumer<TextView>()
consumer.consume(Button(context)) // 👈 equivalent to 'add' of 'List'
Copy the code

In the previous example, when declaring Producer, we already knew that the generic T would only be used as output, but each time we used it, we needed to add an Out TextView to support covariation, which was cumbersome to write.

Kotlin offers an alternative: you can declare a class by adding the out keyword to the generic symbol to indicate that the generic parameter T will only be used for output.

class Producer<out T> {
    fun produce(a): T? {
        return null}}val producer: Producer<TextView> = Producer<Button>() // 👈 do not write out
val textView: TextView? = producer.produce()
Copy the code