從零開始學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
}