【Kotlin】 自学(七)-Kotlin泛型

1,333

泛型基本概念

泛型基础概念

  • 一种类型层面抽象
  • 通过泛型参数实现构造更加通用的类型的能力
  • 可以让符合继承关系的类型批量实现某些能力

泛型基本声明

//函数声明
fun <T> maxOf(a: T, b: T): T {
    TODO()
}
//类声明
public class List<T> {
   TODO()
}

泛型使用

  val max : String = maxOf<String>("Hello", "World")
    
  val list:List<String> = List<String>()

泛型约束

例子

fun <T : Comparable<T>> maxOf(a: T, b: T): T {
    return if (a > b) a else b
}

多个约束

需要使用where,用,隔开

fun <T, R> callMax(a: T, b: T): R
        where T : Comparable<T>, T : () -> R,
              R : Number {
    return if (a > b) a() else b()
}

泛型形变

主要有三种:不变,协变,逆变

  • 不变:没有继承关系

  • 协变:继承关系一致

  • 逆变:继承关系相反

协变

使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型

协变点:函数返回值类型就是泛型参数

//书总类
interface Book
//教育书
interface EduBook : Book
//书店
class BookStore<out T : Book> {
    fun getBook(): T {
        TODO()
    }
}

说明:书可以从教育书店和总书店买到,但是教育书一般只能是教育书店买,总书店买的不一定是

逆变

使用in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型 逆变点:函数参数类型为泛型类型

//垃圾总类
open class Waste
//干垃圾类
class DryWaste : Waste()
//垃圾桶
class Dustbin<in T : Waste> {
    fun put(t: T) {
        TODO()
    }
}

说明:垃圾桶能放干垃圾和所有的垃圾,干垃圾桶只能放干垃圾

星投影

介绍

我们可以使用*替代我们的泛型参数

  • *可用在变量类型声明位置
  • *可以描述一个未知类型
  • *替换类型:协变点返回泛型参数上限类型;逆变点接受参数下限类型;所有类型的下限都是Nothing

举例:

Function<*, String> , 代表 Function<in Nothing, String> ;
Function<Int, *> , 代表 Function<Int, out Any?> ;
Function<, > , 代表 Function<in Nothing, out Any?> 

适用范围

*不能直接用在属性或者函数上

//错误 间接作用属性上
QueryMap<String,*>()
//错误 直接作用到函数上
maxOf<*>(1,2)

*适用于类型描述场景

    val queryMap: QueryMap<*, *> = QueryMap<String, Int>()
    queryMap.getKey()
    queryMap.getValue()

    val f: Function<*, *> = Function<Number, Any>()
	//泛型擦除
    if (f is Function) {
        (f as Function<Number, Any>).invoke(1, Any())
    }
    //这个比较特殊
    HashMap<String, List<*>>()

泛型的实现类和内联特化

伪泛型:编译时擦除类型,运行时无实际类型生成

解决办法:声明内联函数

我们在使用Gson反序列化的时候,一般我们是这样使用的

public <T> T fromJson( Stirng json, Class<T> class){
	...
}

使用内联特化后就可以不传这个Class

inline fun <reified T> Gson.fromJson(json: String): T
        = fromJson(json, T::class.java)
//类型推导
val person: Person = gson.fromJson("""{}""")
//泛型参数
val person2 = gson.fromJson<Person>("""{}""")

案例

typealias OnConfirm = () -> Unit
typealias OnCancel = () -> Unit

private val EmptyFunction = {}

open class Notification(
    val title: String,
    val content: String
)

class ConfirmNotification(
    title: String,
    content: String,
    val onConfirm: OnConfirm,
    val onCancel: OnCancel
) : Notification(title, content)

interface SelfType<Self> {
    val self: Self
        get() = this as Self
}

open class NotificationBuilder<Self: NotificationBuilder<Self>>: SelfType<Self> {
    protected var title: String = ""
    protected var content: String = ""

    fun title(title: String): Self {
        this.title = title
        return self
    }

    fun content(content: String): Self {
        this.content = content
        return self
    }

    open fun build() = Notification(this.title, this.content)
}

class ConfirmNotificationBuilder : NotificationBuilder<ConfirmNotificationBuilder>() {
    private var onConfirm: OnConfirm = EmptyFunction
    private var onCancel: OnCancel = EmptyFunction

    fun onConfirm(onConfirm: OnConfirm): ConfirmNotificationBuilder {
        this.onConfirm = onConfirm
        return this
    }

    fun onCancel(onCancel: OnCancel): ConfirmNotificationBuilder {
        this.onCancel = onCancel
        return this
    }

    override fun build() = ConfirmNotification(title, content, onConfirm, onCancel)
}

fun main() {
    ConfirmNotificationBuilder()
        .title("Hello")
        .onCancel {
            println("onCancel")
        }.content("World")
        .onConfirm {
            println("onConfirmed")
        }
        .build()
        .onConfirm()
}