Advanced Kotlin - Generics

We often use generics in Android development, such as: List, Map, Set, Adapter, etc., so generics are also supported in Kotlin.
What is generic?
Generics: Defer the work of type definition until when creating an object or calling a method to specify a specific type.

1. Define generic methods in Kotlin

There are two ways to define generics in Kotlin as in Java:

  • defined on the class
  • Defined in a function
    Defined on a class Example:
class MagicBox<T>(val item: T) {
    var available = false

    fun fetch(): T? {
        return item.takeIf { available }
    }
}

defined in the function

class MagicBox<T>(val item: T) {
    var available = false

    /**
     * 函数中增加范式类型,类似Java
     */
    fun <R> fetch(anotherGenericity: (T) -> R): R? {
        return anotherGenericity(item).takeIf { available }
    }
}

2. Define constrained generics in Kotlin

The definition of constrained generics in Kotlin < T extend XXX>is similar to the way Java defines generics, which means that the type specified by the generic must be a class of the specified type or a subclass that inherits the class of the specified type. This sentence is a bit confusing, for example: define a generic type class A<T : B>(), the incoming generic type T must be B or a subclass of B. A complete example is as follows:

//定义一个约束泛型
private class ConstraintMagicBox<T:Human>(item:T){

}

//定义一个父类
private open class Human(val name: String,val age:Int)
//定义一个Human类的子类
private class Male(name: String,age: Int):Human(name, age)
//定义一个Human类的子类
private class Female(name: String,age: Int):Human(name, age)

fun main() {
    //同为Human类型的可传入
    val male = ConstraintMagicBox(Male("Jack", 20))
    val female = ConstraintMagicBox(Female("Jim", 20))
}

3. Kotlin defines variable parameter generics

This is similar to defining variable parameters ( private void method(int.. num)) for methods in Java. The definition method in Kotlin uses the keyword ** vararg**, which can be used for both ordinary functions and constructors.
Examples are as follows:

private class VarMagicBox<T : VarHuman>(vararg val items: T) {

    //根据参数从items中获取数据,其中items类型未Array
    fun fetch(index: Int): T? {
        return items.getOrNull(0)
    }

    //在函数中增加可变参数
    fun fetch(vararg indexs: Int):List<T>{
        indexs.takeIf {
            indexs.isNotEmpty()
        }.run {
            return items.filterIndexed { index, t ->
                indexs.contains(index)
            }
        }
    }
}

private open class VarHuman(val name: String, val age: Int)

4. Use in and out to modify generics in Kotlin

In defining a List in Java, specify a specific type. When creating an instance, only instances of this type can be new, and instances of subclasses or parent classes cannot be new. For example: or these two ways of writing are not supported
in
ArrayList<String> list = new ArrayList<CharSequence>()
JAVA
ArrayList<CharSequence> list = new ArrayList<String>()
. The correct way of writing is: ArrayList<String> list = new ArrayList<String>().
But support can be done in Kotlin .
Of course, the generic examples defined above are also not supported, so how can Kotlin support such a way of writing?
Because there are two important keywords in Kotlin in(协变),out(逆变)
let's see how the latter two keywords can support the above-mentioned writing method.
The usage is very simple, let's look at the example first, and see how to use it:

//out修饰的泛型 仅将泛型作为函数返回值
//作用:让子类泛型对象可以赋值给父类泛型对象
interface OutTest<out T>{
    fun outTest():T
}

//in修饰的泛型 仅将泛型作为函数参数,泛型无法当做返回值
//作用:让父类泛型对象可以赋值给子类泛型对象
interface InTest<in T>{
    fun inTest(param : T)
}

The test code is as follows:

open class Food()
open class FastFood():Food()
class Hamburg():FastFood()

class FastFoodStore() : OutTest<FastFood>{
    override fun outTest(): FastFood {
        println("FastFoodStore ----------")
        return FastFood()
    }
}

class HamburgStore():InTest<FastFood>{
    override fun inTest(param: FastFood) {
        println("HamburgStore-----------")
    }
}

fun main() {
    //子类对象可以传给父类泛型对象 out
    val food1 : OutTest<Food> = FastFoodStore()

    //父类对象可以传给子类泛型对象 in
    val food2 : InTest<Hamburg> = HamburgStore()
}

关键字in、out使用总结
There are two main points:

  • outModified generics can only 函数返回值be used in , and inmodified generics can only 函数的参数be used in ;
  • outModified generics can only be used 子类泛型对象赋值给父类泛型对象, and inmodified generics can only be used 父类泛型对象赋值给子类泛型对象, as shown in the figure below;
    image.png

Welcome to leave a message and learn from each other!


Example source address kotlin_demo

Guess you like

Origin blog.csdn.net/seevc/article/details/123007514