kotlin 泛型

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<?>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 本博文主要讲解一些Kotlin泛型的问题,中间会对比穿插Java泛型。 1. 泛型类型参数 1.1 形式 我们使用...
    24K男阅读 18,403评论 4 11
  • 建议先阅读我的上一篇文章 -- Java 泛型 和 Java 泛型一样,Kotlin 泛型也是 Kotlin 语言...
    JohnnyShieh阅读 6,440评论 1 26
  • 写在开头:本人打算开始写一个Kotlin系列的教程,一是使自己记忆和理解的更加深刻,二是可以分享给同样想学习Kot...
    胡奚冰阅读 1,362评论 1 3
  • 转载文章,出处: https://blog.kotliner.cn/2017/06/26/kotlin-gener...
    _10_01_阅读 659评论 0 0
  • 泛型 泛型(Generic Type)简介 通常情况的类和函数,我们只需要使用具体的类型即可:要么是基本类型,要么...
    Tenderness4阅读 1,385评论 4 2