Kotlin基础十

276 阅读5分钟

内容:

  • 泛型类型必须有明确类型
  • 泛型属性,泛型方法,泛型类
  • 运行时泛型擦除
  • 不擦除泛型的特殊情况
  • 类型和类的关系

一泛型

1.1泛型类型必须有明确类型

在kotlin中泛型也支持类型自动推导,并且kotlin中的泛型要么显示的指出,要么类型自动推导出,但是不支持原生态类型。

//泛型类型自动推导出
val arr1 = arrayListOf("1", "2")
//泛型类型显示的写出
val arr2 = arrayListOf<String>("1", "2")
//错误代码----不支持原生态类型
val arr3 = ArrayList()

这和java中的不同,因为在java中jdk1.5才有了泛型的引入,所以java为了兼容以前的代码,才支持原生态类型(就是不写泛型),而kotlin从一开始就有泛型,所以泛型必须有明确的类型。

1.2泛型函数和属性

泛型函数:

fun <T> List<T>.getSum(range: IntRange): List<T> {
    return this.subList(0, range.last)
}

泛型函数可以是成员函数,接口函数,扩展函数,顶层函数。这和java一样。

泛型属性:

val <T >List<T>.penultimate: T
    get()
    = this[size - 2]

注意:不能单独的在非扩展属性中使用泛型,类似这样的代码就会报错:

val <T>penultimate: T = ...

但是可以结合类的泛型声明一个泛型属性,类似这样是可以的

open class Father<P> {
    var mPersenter: P? = null
}

1.3泛型类

上边就呈现出来一个泛型类,它和java的写法一样,不同的是他的子类必须提供泛型类型,而在本类中可以当成普通类型使用这个类型p。

我们还可以继承这个类继续使用泛型,类似这样的代码

class Son<P> : Father<P>() {
}

1.4泛型约束

我们可以为上边的泛型添加一个约束,让这个泛型只能是某些类型:

class Son<P:String> : Father<P>() {
}

这里指定泛型只能是字符串类型。

1.5泛型非空类型约束

我们在基础六的时候已经说过所有的泛型编译时候都会编译成Any?,当时我们说如果不想要这个编译成可空类型,需要让泛型继承Any,那里继承的Any就是泛型约束。

1.6运行时泛型擦除

我们知道java中泛型在运行是都是擦除的,同样的Kotlin中的泛型运行时也是擦除的。

val arr = arrayListOf("a", "b", "c")
if (arr is List<String>){
}

上边的代码编译是错误的。因为已经擦除,并不能知道集合里边的类型是什么。所以只能用*号做为通配符。

同样的:

val arr = arrayListOf("a", "b", "c")
val arr2 = arr as? ArrayList<Int>

这行代码不会报错,因为他根本没有去检查集合里边的类型倒是是不你要求的类型,泛型擦除了,也没有办法检查。

1.7不擦除泛型的特殊情况

默认情况下泛型在运行时候都是被擦除的,但是有一种特殊的情况,泛型运行时候是不擦除的。内联函数:

inline fun <reified T> isString(value: Any) = value is T

这个时候泛型是不会被擦除的。

Kotlin标准库中提供的一个方法filterIsInstance(),他的用法如下代码:

val arr = arrayListOf(1, "a", "b")
//调用
val arr2 = arr.filterIsInstance<String>()
//打印arr2的结果如下
["a", "b"]

这里的泛型管用,说明就是用的不擦除泛型的特殊情况。

二类和类型和子类型的关系

看下边一些代码:

open class Animals {
}
class Cat :Animals()

不管在Kotlin还是在java中:ArrayList<Animals> 与ArrayList<Cat>之间没有继承关系。

而对于Kotlin中我们可以定义一个协变类,来保存Animals和Cat之间的继承关系。一个协变类一定是一个泛型类。

class Herd<out T:Animals> {

}

这个时候Head<Animals>和Head<Cat>就有继承关系。这个类就叫做协变类。

需要注意你不能把任何类都变成协变类。因为在该类里边你不能做为参数使用这个T。如这样写代码就是错误的:

class Herd<out T : Animals> {
    //错误代码,不能用到参数中,也就是in位置
    fun get(a: T) = {}
}
类型参数T上的关键宇out有两层含义:
  • 子类型化会被保留(Head<Cat>是Head<Animals>的子类型)
  • T只能用在out位置,不能做为参数
构造方法的参数既不在in位置,也不在out位置。

小结:

  • Kotlin中的泛型必须明确显示的声明出来
  • 默认情况下Kotlin中的泛型在编译器擦除
  • 特殊情况下,泛型不会被擦除,内联时候
  • 可以声明一个类在某个类型参数上是协变的,如果该参数只是用在out位置
  • Kotlin中的只读接口List声明成了协变的,这意味着List<String>是List<Any>的子类型
  • *在泛型中的语法和java中的?的语法是一样的

          Kotlin关于反射和注解的一些细节,这里不打算再叙述,到此关于Kotlin的基础内容结束。回想拜读Kotlin实战这本书的心里过程,坚持一件事情真的是一件非常痛苦是事情,期间感谢zhangwenhaojf40it 不断的精神鼓励,不断给我精神食粮,才有了这些读书笔记。最后,希望这些文章能帮助到那么想入门Kotlin的人。