美文网首页
kotlin 泛型

kotlin 泛型

作者: 笔云客 | 来源:发表于2018-12-31 11:27 被阅读0次

    fun <T> List<T>.slice(indices: IntRange): List<T>
    <T>: 类型函数声明
    List<T>: 接收者和返回类型使用了类型形参

    (1)类型参数约束
    表示上界约束时: fun <T: Number> List<T>.sum() :T
    T : Number 中
    T是类型参数,Number是上界

    一旦使用了类型形参T的上界,你就可以把类型T的值当作它的上界(类型)的值使用。
    如:
    // 指定Number为类型形参的上界
    fun <T:Number> oneHalf(value: T): Double{
    // 调用Numbe类中的方法
    return value.toDouble()
    }

    (2)声明带类型参数约束的函数
    // 这个函数的实参必须是可比较的元素
    fun <T: Comparable<T>> max(first: T, second: T): T{
    return if (first > second) first else second
    }
    如果传入无法比较的参数时,代码将无法编译:
    max("Kotlin", 42) // 抛出异常

    (3)为一个类型参数定义多个约束
    where关键字 为一个类型参数定义多个约束时使用
    // 这里为类型T指定多个约束 ,where 后就是约束
    fun <T> ensureTrailingPeriod(seq: T) where T : CharSequence, T : Appendable{
    // 调用 Appendable 接口的方法
    if (!seq.endsWith('.')){
    seq.append('.')
    }
    }

    (4)让类型形参非空

    // 这里声明的泛型函数是没有指定上界的,但是内部机制会吧这个泛型
    // 函数默认使用 Any? 作为上界
    class Processo<T> {
    // 这里的 value 是可空的,尽管T没有使用问号标记
    fun process(value: T){
    value?.hashCode()
    }
    }
    fun testPro(){
    // 可用类型 String 用来替换T
    val mullableStringProcesso = Processo<String?>()
    // 使用 “null” 作为 “value”实参代码可以编译
    mullableStringProcesso.process(null)
    }

    设置泛型始终都为不可空的
    // 这里设置上界为 Amy ,确保了T类型永远都是非空类型
    class Processo<T:Any> {
    fun process(value: T){
    value?.hashCode()
    }
    }
    fun testPro(){
    // 由于泛型的上界为 Any 不为空,所以无法设置泛型类型为可空类型
    val mullableStringProcesso = Processo<String?>()

    }

    (5)运行时的泛型:擦出和实例化类型参数
    Jvm上的泛型一般是通过类型擦除实现的。

    (6)类型的检查和转换
    如何检查一个值是否是列表:
    如:
    if (value is List<*>) { ... }
    想要检查一个值是否为列表,不可以在列表声明使用具体类型的列表,必须使用 * 来代替泛型类型才可以对这个值进行检查
    如:
    if( value is List<String>) { ... } // 这里使用了具体的 String,所以代码不会编译

    " * " : 称为星号投影

    (7)对未知泛型类型 和 已知类型做类型转换
    如:
    // 这里 c 的泛型类型 是未知的
    fun printlnSum( c: Collection<*> ){
    val intList = c as? List<Int> ? :throw IllegalArgumentException("List is expected")
    println( inList.sum() )
    }
    调用:
    println( setOf(1, 2, 3)) // Set不是列表,抛出异常
    println(listOf("a", "b", "c" )) // 类型转换成功,但后面抛出了另外的异常

    如果吧 * 号换成具体类型,那么转换就可以成功

    (8)声明带实化类型参数的函数
    fun <T> isA(value : Any) = value is T
    // 这段代码是无法编译的,因为无法知道泛型的类型

    reified: reified 的意思是实化(具体化)。而作为 Kotlin 的一个方法 泛型 关键字,它代表你可以在方法体内访问泛型指定的JVM类对象。必须以 inline 内联方式声明这个方法才有效。

    理解: 内联函数的类型实参能够被实化,意味着你可以在运行时引用实际的类型实参,reified 关键字的声明就是让当前泛型类型可以在运行时得到具体的类型,就是类型参数不会在运行时擦除。

    加上inline reified 后,函数可以编译
    // 声明带实体化类型参数的函数
    // reified T : 可以在运行时得到这个泛型的类型
    inline fun <reified T> isA(value: Any) = value is T

    (9)使用标准库的函数

    val items = listOf("one" ,2 ,"three")
    /*
    使用它来检查列表中的值是不是指定为该类型实参的类实例

    println(items.filterIsInstance<String>())
    [one, three]
    */

    // "reified" 声明了类型参数不会在运行时擦除
    inline fun <reified T> Iterable<*>.filterIsInstance() : List<T>{
    val destination = mutableListOf<T>()
    // 迭代当前列表实例中的元素并放入element
    for (element in this) {
    // 可以检查元素是不是为指定为类型实参类的实例
    if (element is T){
    destination.add(element)
    }
    }
    return destination
    }

    为什么实化只对内联函数有效:
    编译把实现内联函数的字节码插入每一次调用发生的地方,每次你调用带实化类型参数的函数时,编译器都知道这次特定调用中用作列席实参的确切类型。因此,编译器都知道这次特定调用中作为类型实参的具体字节码。
    如:
    filterIsInstance<String>

    for (element in this){
    if(element is String){
    destination.add(element)
    }
    }
    着两段代码时等价的

    (10)调用实化类型参数代替类引用
    val serviceImpl = ServiceLoader.load(Service::class.java)
    :: class.java 的语法展现了如何获取java.lang.Class 对应的Kotlin类

    使用带实化参数的函数重写这个例子
    val serviceImpl = loadService<Service>()

    具体实现
    // 类型参数标记成了 “reified”
    inline fun <reified T: Activity> Context.startActivity(){
    // 把T::class 当成类型参数的类访问
    val intent = Intent(this, T::class.java)
    startActivity(intent)
    }

    oninline: 这个标记关键字可以把内联函数的某段语句或者字段标记为非内联的

    (11)类,类型和子类型;

    子类:如B 实现了 A 就等于B 是A 的子类

    类型:两种对象的分类,如Int 是 Number类型,反过来Number是Int类型

    子类型:如:Int? 可以存入 整型数据和null , int 只可以存入整型数据,所以Int 是 Int? 的子类型

    (12)协变
    如果A是B的子类型,那么List<A> 就是 List<B>的子类型,这样的类或者接口被称为协变的。

    函数的参数类型叫做in位置,函数的返回类型叫做out位置。

    协变:
    open class Animal{}
    class Herd<T: Animal>{}
    fun feedAll(animals: Herd<Animal>){}

    class Cat: Animal(){}
    fun takeCareOfCats(cats: Herd<cat>){
    // 这里即使cats 的类型是Herd<Cat> 类型的,而Cat又是继承Animal类,也无法编译
    feedAll(cats)
    }

    如果把 Herd<T: Animal> 的T前面加上 out 代码就可以编译成功

    重申:(关键字 out)
    Herd<out T: Animal >时,子类型化会被保留(producer<Cat>)是Producer<Animal>的子类型

    // MutableList不能在T上声明成协变的
    interface MuableList<T>:List<T>, MutableCollection<T>{
    // 因为T用在了in位置
    override fun add(element: T): Boolean
    }

    (13)逆变:反转子类型化关系(关键字 in)
    逆变: 如果B 是A 的子类型,那么Comsumer<A>就是 Comsumer<B>的子类型。,类型A和类型B交换了位置,所以我们说子类型被反转了。

    协变,逆变总结:fun transform(t: T): T ,t : T 这个T是in位置,返回值类型的T是out位置

    Kotlin的
    MutableList<out T> 和java 中的 MutableList< ? extends T> 是一个意思
    MutableList<in T> 和Java 中的MutableList<? super T> 是一个意思

    协变:
    producer<out T>
    类的子类型化保留了:producer<Car>是Producer<Animal>的子类型(可以使用T的子类型)
    T只能在out位置
    逆变:
    Consumer<in: T>
    子类型化反转了: Comsumer<Animal>是Consumer<Cat>的类型(可以使用T的父类型)
    T只能在in位置
    不变型:
    MutableList<T>
    没有子类型化
    T可以在如何位置

    使用点变型:在类型出现的地方指定变型
    使用不变型类型参数的数据拷贝函数 ,能够把一个列表中的元素拷贝到拎一个列表
    引入一个泛型:
    fun <T> copyData(source: MutableList<T>), destination: MutableList<T>{
    for(Item in source){
    destination.add(item)
    }
    }

    引入多个泛型:
    fun <T: R, R> copyData(source: MutableList<T>, destination: MutableList<R>){
    for(Item in source){
    destination.add(item)
    }
    }

    带out投影类型的数据拷贝函数:
    // 可以给类型的用法加上out 关键字,没有使用那些T 用在 in 位置的方法
    fun <T> copyData(source: MuableList<out T> , destination: MutableList<T>){
    for(item in source){
    destination.add(item)
    }
    }

    带in投影类型参数的数据拷贝函数
    // 允许目标元素的类型是来源元素类型的超类型
    fun <T> copyData(source: MutableList<T> , destination: MutableList<in T>){
    for(item in source){
    destination.add(item)
    }
    }

    (14)星号投影
    写法一:List<*> 一个包含未知类型元素的列表,相当于Java的List<?>

    相关文章

      网友评论

          本文标题:kotlin 泛型

          本文链接:https://www.haomeiwen.com/subject/ebuilqtx.html