从零开始学Kotlin-泛型(8)

从零开始学Kotlin基础篇系列文章

与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。

泛型类的基本使用

  • 泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上
    class DemoClass<T>(date: T) {//date是任意类型的,避免强转
        var todayDate=date
    }
  • 创建类的实例时我们需要指定类型参数
    //指定泛型date为String,三种创建方法
    val demo1: DemoClass<String> = DemoClass<String>("2018-01-27")
    val demo2: DemoClass<String> = DemoClass("2018-01-27")
    val demo3 = DemoClass("2018-01-27")
    //指定泛型date为Int,三种创建方法
    val demo1: DemoClass<Int> = DemoClass<Int>(20180127)
    val demo2: DemoClass<Int> = DemoClass(20180127)
    val demo3 = DemoClass(20180127)

泛型方法的基本使用

  • Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:
    fun <T> showMsg(msg: T) {
    }
  • 在调用泛型函数时,如果可以推断出类型参数,可以省略泛型参数
    val msg = showMsg("泛型的使用")
    val msg1 = showMsg(200)
    val msg2 = showMsg<String>("指定返回类型")
  • 泛型结合when控制语句实例
    fun <T> showPrint(printMsg: T) {
        when (printMsg) {
            is Int -> println("printMsg是Int类型:$printMsg")
            is String -> println("printMsg是String类型:$printMsg")
            else -> println("printMsg类型不是Int也不是String:$printMsg")
        }
    }

    fun main() {
        showMsg(100)
        showMsg("2017-01-27")
        showMsg(true)
    }

泛型约束

  • 对于给定的参数, 所允许使用的类型, 可以通过泛型约束(generic constraint) 来限制。冒号之后指定的类型就是类型参数的上界(upper bound), 对于类型参数 T , 只允许使用 Comparable<T>的子类型
    fun <T : Comparable<T>> sort(list: List<T>) {//上界约束
    }

    fun main() {
        sort(listOf(1, 2, 3))//正确
        sort(listOf("1", "2", "3"))//正确
        sort(listOf(HashMap<Int,String>))//错误, HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型
    }
  • 默认的上界是 Any?,对于多个上界约束条件,可以用 where 子句:
    //多个约束,T有多个上限 , where T:类型,T:类型
    fun <T> getBetterBig(list: Array<T>, threhold: T): List<T> where T : Number, T : Comparable<T> {
        return list.filter { it >= threhold }.sorted()
    }

泛型协变

  • Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。
  • 声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。
  • out 修饰符;这里比较难理解,先举一个例子
    //创建两个类,继承关系
    open class Person(name: String)
    open class Student(name: String) : Person("PersonA")
    class Teacher(name: String) : Student("StudentA")

    fun main() {
        var person = Person("PersonA")
        var personList: ArrayList<Person> = arrayListOf(person)

        var student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)

        var teacher = Teacher("TeacherA")
        var teacherList: ArrayList<Teacher> = arrayListOf(teacher)

        for (name in personList.withIndex()) {
            println("name is $name")//输出:name is PersonA
        }

        for (name in studentList.withIndex()) {
            println("name is $name")//输出:name is StudentA
        }
        for (name in teacherList.withIndex()) {
            println("name is $name")//输出:name is TeacherA
        }

        person = student//正确,因为 Student 是 Person 的子类
        /*
        编译报错,类型不匹配:Required ArrayList<Person> Found ArrayList<Student>
        这是因为,虽然 Student 是 Person 的子类,但是 ArrayList<Student> 并不是 ArrayList<Person> 的子类
         */
        personList = studentList//错误
    }
  • 对于上面的编译错误可以使用 协变注解修饰符 out 进行类型修饰。 协变类型参数 out 相当于java中的ArrayList<? extends C>;协变类型参数只能用作输出,可以作为返回值类型,但是无法作为入参的类型
    fun main() {
        var person = Person("PersonA")
        var personList: ArrayList<out Person> = arrayListOf(person)//使用 out 修饰符,限定类型上限

        var student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)

        personList = studentList//编译正确,这是因为 ArrayList<out Person> 限定了子类的上限为 Person

        for (name in personList.withIndex()) {
            println("name is $name")//输出:name is StudentA
        }
    }
  • in 修饰符,同样先看一个例子
    fun main() {
        var person = Person("PersonA")
        var personList: ArrayList<Person> = arrayListOf(person)

        var student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)

        var teacher = Teacher("TeacherA")
        var teacherList: ArrayList<Teacher> = arrayListOf(teacher)

        /*
        以下两种均报类型不匹配错误,
         */
        teacherList = personList//Required ArrayList<Teacher> Found ArrayList<Person>
        teacherList = studentList//Required ArrayList<Teacher> Found ArrayList<Student>
    }
  • 对于上面的编译错误可以使用 协变注解修饰符 in 进行类型修饰。<in Class> 相当于 Java 中的 ArrayList<? super Class> ;in 修饰符使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型,但是无法作为返回值的类型;
    fun main3() {
        val person = Person("PersonA")
        val personList: ArrayList<Person> = arrayListOf(person)

        val student = Student("StudentA")
        val studentList: ArrayList<Student> = arrayListOf(student)

        val teacher = Teacher("TeacherA")
        var teacherList: ArrayList<in Teacher> = arrayListOf(teacher)// <in Teacher> 就是允许 Teacher 的超类类型下限为 Teacher

        for (name in teacherList.withIndex()) {
            println("name is $name")//输出:name is TeacherA
        }

        teacherList = personList
        for (name in teacherList.withIndex()) {
            println("name is $name")//输出:name is PersonA
        }

        teacherList = studentList
        for (name in teacherList.withIndex()) {
            println("name is $name")//输出:name is StudentA
        }
    }
  • 再来理解消费者 in 只能用作输入和 生产者 out 只能用作输出的概念:

不使用 in 和 out 修饰时

     open class Person(name: String) {
            var myName = "Siberiadante"
        }
     class Student(name: String) : Person("PersonA")

    fun main() {
        val person = Person("PersonA")
        var personList: ArrayList<Person> = arrayListOf(person)

        val student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)

        personList.add(student)//set 设置值,编译通过
        personList[0].myName// get 取值,编译通过
    }

作为 < out T>的类型,由于所有类型均为T的下限,无法得知其确定的类型,所以不能使用 set 方法,只能 get

    fun main() {
        val person = Person("PersonA")
        var personList: ArrayList<out Person> = arrayListOf(person)

        val student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)
        /*
        prohibits(禁止) use of public open fun add(element:E) !
         */
        personList.add(student)// set 设置值,编译不通过
        personList[0].myName// get 取值,编译通过
    }

作为 < in T>的类型

    fun main() {
        val person = Person("PersonA")
        var personList: ArrayList<in Person> = arrayListOf(person)

        val student = Student("StudentA")
        var studentList: ArrayList<Student> = arrayListOf(student)

        personList.add(student)//set 设置值,编译通过
        /*
        Unresolved reference : name,
         */
        personList[0].myName// get 取值,编译不通过
    }

星投射

  • 在我们不知道类型参数的任何信息的情况下, 仍然希望能够安全地使用它时,就可以使用类型投射
    var list:ArrayList<*> = arrayListOf(100)
    fun main() {
        val person = Person("PersonA")

        val student = Student("StudentA")
        val studentList: ArrayList<Student> = arrayListOf(student)

        /*
        相当于  var personList: ArrayList<out Person> = studentList
         */
        var personList: ArrayList<*> = studentList
    }
    fun main9() {
        val person = Person("PersonA")
        val personList: ArrayList< Person> =arrayListOf(person)

        val student = Student("StudentA")

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,297评论 18 399
  • 面向对象编程(OOP) 在前面的章节中,我们学习了Kotlin的语言基础知识、类型系统、集合类以及泛型相关的知识。...
    Tenderness4阅读 4,359评论 1 6
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,064评论 9 118
  • 酒沁人心 2个小时化解一杯十几年的南方沉酒。端起碗喝,香沉入柔,甜而不腻,一口接一口,跟父亲说好只喝一半,剩下的倒...
    吉草末阅读 197评论 0 0
  • 有时候观察人也很有意思,来这里学习一段时间,我由一个局外人的角度观察科室的医生,每个人都各自有其性格特征。科主任杨...
    袁澜月阅读 626评论 0 1