kotlin泛型


/**
 * 泛型类
 */

class Animal<T> {}

/**
 * 泛型接口
 * @param T
 */
interface IAnimal<T> {
    fun <T> initAnimal(p: T)
}

//泛型约束
//泛型约束表示我们可以指定泛型类型(T)的上界,
//即父类型,默认的上界为Any?,如果只有一个上界可以这样指定
fun <T : Animal<T>> init(p: T) {}

//如果需要指定多个上界类型,就需要使用where语句:
fun <T> initA(p: T) where T : Animal<T>, T : IAnimal<T> {}

//类型擦除
//Kotlin 为泛型声明执行的类型安全检测仅在编译期进行,
 运行时实例不保留关于泛型类型的任何信息。这一点在 Java 中也是类似的。
//
//例如,Array<String>、Array<Int>的实例都会被擦除为Array<*>,
//这样带来的好处是保存在内存中的类型信息也就减少了。
//
//由于运行时泛型信息被擦除,所以在运行时无法检测一个
//实例是否是带有某个类型参数的泛型类型,
//所以下面的代码是无法通过编译的
(Cannot check for instance of erased type: Array<Int>):

//fun isArrays(a: Any) {
//  if (a is Array<Int>) {
//    println("is array")
//  }
//}
//但我们可以检测一个实例是否是数组,
//虽然 Kotlin 不允许使用没有指定类型参数的泛型类型,
//但可以使用星投影*(这个后边会说到):

fun isArray(a: Any) {
    if (a is Array<*>) {
        println("is array")
    }
}
//同样原因,由于类型被擦除,我们也无法安全
//的将一个实现转换成带有某个类型参数的泛型类型:

fun sumArray(a: Array<*>) {
    val intArray = a as? Array<Int> ?: throw IllegalArgumentException("Array的泛型类型必须是Int类型")
    println(intArray.sum())
}
//因为我们无法判断数组a的是不是Array<Int>类型的,
//所以可能会出现异常的情况。
//
//对于泛型函数,如果在函数内需要使用具体的泛型类型,
//同样由于运行时泛型信息被擦除的原因,
//你无法直接使用它(Cannot check for instance of erased type: T):

//fun < T> test(param: Any) {
//  if (param is T){
//    println("param type is match")
//  }
//}
//但还是有办法的,可以用inline关键字修饰函数,
//即内联函数,这样编译器会把每一次函数调用都换成函数实际代码实现,
//同时用reified关键字修饰泛型类型,这样就能保留泛型参数的具体类型了:
inline fun <reified T> test(param: Any) {
    if (param is T) {
        println("param type is match")
    }
}
//型变
//1、声明处型变
//型变是泛型中比较重要的概念,首先我们要知道 Kotlin 中
//的泛型是不型变的,这点和 Java 类似。那什么是型变呢,看个例子:

open class A
class B : A()


fun text() {
    val arrayB: Array<B> = arrayOf(B(), B())
    //val arrayA:Array<A> = arrayB
//    你会发现第二个赋值语句会有错误提示,
//Type mismatch. Required:Array<A> Found:Array<B>类型不匹配,
//    Array<B>并不是Array<A>的子类,
就是因为 Kotlin 中的泛型是默认不型变的,
无法自动完成类型转换,但B是A的子类,
//    这个赋值操作本质上是合理的、安全的,
//但编译器似乎并不知道,这必然给我们开发过程中带来了麻烦

//    为什么Array无法正常的赋值,而List、Set、
//Map可以呢?如下代码,编译器不会有错误提示的:
//
//    val list1: List<B> = listOf(B(), B(), B())
//    val list2: List<A> = list1
//    我们可以对比一下Array和List在源码中的定义:
//
//    public class Array<T> {}
//
//    public interface List<out E> : Collection<E> {}

//    可以看到List的泛型类型使用了out修饰符,这就是关键所在了。
//这就是 Kotlin 中的声明处型变,用来向编译器解释这种情况。
//
//    关于out修饰符我们可这样理解,当类、
//接口的泛型类型参数被声明为out时,
//则该类型参数是协变的,泛型类型的子类型是被保留的,
//    它只能出现在函数的输出位置,只能作为返回类型,
//即生产者。带来的好处是,A是B的父类,
那么List<A>可以是List<B>的父类。

    // 我们修改下上边List赋值的代码:

    val list1: List<A> = listOf(A(), A(), A())
//    val list2: List<B> = list1
//    即反过来赋值,由于B并不是A的父类,
//会有Type mismatch. Required:List<B> Found:List<A>错误提示。
//    为了应对这种情况,Kotlin 还提供了一个in修饰符。

//    关于in修饰符我们可这样理解,当类、接口的泛型类型参数被声明为in时,
//则该类型参数是逆变的,泛型类型的父类型是被保留的,
//    它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者。
//    其实 Kotlin 中的Comparable接口使用了in修饰符:

//    写一个测试函数,编译器并不会报错:

    fun test(a: Comparable<A>) {
        val b: Comparable<B> = a
    }
   // 所以in修饰符和out修饰符的作用看起来的相对的,
//A是B的父类,那么Comparable<B>可以是Comparable<A>的父类,体会下区别。

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

推荐阅读更多精彩内容