從零開始學Kotlin-泛型(8)

從零開始學Kotlin基礎(chǔ)篇系列文章

與 Java 一樣,Kotlin 也提供泛型,為類型安全提供保證,消除類型強轉(zhuǎn)的煩惱。

泛型類的基本使用

  • 泛型,即 "參數(shù)化類型",將類型參數(shù)化,可以用在類,接口,方法上
    class DemoClass<T>(date: T) {//date是任意類型的,避免強轉(zhuǎn)
        var todayDate=date
    }
  • 創(chuàng)建類的實例時我們需要指定類型參數(shù)
    //指定泛型date為String,三種創(chuàng)建方法
    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,三種創(chuàng)建方法
    val demo1: DemoClass<Int> = DemoClass<Int>(20180127)
    val demo2: DemoClass<Int> = DemoClass(20180127)
    val demo3 = DemoClass(20180127)

泛型方法的基本使用

  • Kotlin 泛型函數(shù)的聲明與 Java 相同,類型參數(shù)要放在函數(shù)名的前面:
    fun <T> showMsg(msg: T) {
    }
  • 在調(diào)用泛型函數(shù)時,如果可以推斷出類型參數(shù),可以省略泛型參數(shù)
    val msg = showMsg("泛型的使用")
    val msg1 = showMsg(200)
    val msg2 = showMsg<String>("指定返回類型")
  • 泛型結(jié)合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)
    }

泛型約束

  • 對于給定的參數(shù), 所允許使用的類型, 可以通過泛型約束(generic constraint) 來限制。冒號之后指定的類型就是類型參數(shù)的上界(upper bound), 對于類型參數(shù) 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()
    }

泛型協(xié)變

  • Kotlin 中沒有通配符類型,它有兩個其他的東西:聲明處型變(declaration-site variance)與類型投影(type projections)。
  • 聲明處的類型變異使用協(xié)變注解修飾符:in、out,消費者 in, 生產(chǎn)者 out。
  • out 修飾符;這里比較難理解,先舉一個例子
    //創(chuàng)建兩個類,繼承關(guān)系
    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//錯誤
    }
  • 對于上面的編譯錯誤可以使用 協(xié)變注解修飾符 out 進行類型修飾。 協(xié)變類型參數(shù) out 相當于java中的ArrayList<? extends C>;協(xié)變類型參數(shù)只能用作輸出,可以作為返回值類型,但是無法作為入?yún)⒌念愋?/li>
    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>
    }
  • 對于上面的編譯錯誤可以使用 協(xié)變注解修飾符 in 進行類型修飾。<in Class> 相當于 Java 中的 ArrayList<? super Class> ;in 修飾符使得一個類型參數(shù)逆變,逆變類型參數(shù)只能用作輸入,可以作為入?yún)⒌念愋?,但是無法作為返回值的類型;
    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 只能用作輸入和 生產(chǎn)者 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 設(shè)置值,編譯通過
        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 設(shè)置值,編譯不通過
        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 設(shè)置值,編譯通過
        /*
        Unresolved reference : name,
         */
        personList[0].myName// get 取值,編譯不通過
    }

星投射

  • 在我們不知道類型參數(shù)的任何信息的情況下, 仍然希望能夠安全地使用它時,就可以使用類型投射
    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
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,706評論 18 399
  • 面向?qū)ο缶幊蹋∣OP) 在前面的章節(jié)中,我們學習了Kotlin的語言基礎(chǔ)知識、類型系統(tǒng)、集合類以及泛型相關(guān)的知識。...
    Tenderness4閱讀 4,621評論 1 6
  • 前言 人生苦多,快來 Kotlin ,快速學習Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,701評論 9 118
  • 酒沁人心 2個小時化解一杯十幾年的南方沉酒。端起碗喝,香沉入柔,甜而不膩,一口接一口,跟父親說好只喝一半,剩下的倒...
    吉草末閱讀 239評論 0 0
  • 有時候觀察人也很有意思,來這里學習一段時間,我由一個局外人的角度觀察科室的醫(yī)生,每個人都各自有其性格特征。科主任楊...
    袁瀾月閱讀 720評論 0 1

友情鏈接更多精彩內(nèi)容