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

從零開始學Kotlin基礎篇系列文章

與 Java 同樣,Kotlin 也提供泛型,爲類型安全提供保證,消除類型強轉的煩惱。java

泛型類的基本使用

  • 泛型,即 "參數化類型",將類型參數化,能夠用在類,接口,方法上
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 的子類型
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 進行類型修飾。 至關於 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 只能用做輸出的概念:git

    不使用 in 和 out 修飾時github

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
    }
相關文章
相關標籤/搜索