从零开始学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的子类型
    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 只能用作输出的概念:

不使用 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
}

从零开始学Kotlin-泛型(8)的更多相关文章

  1. 从零开始学Kotlin第六课

    Kotlin调用java代码: 1.如果是内部工程的类,直接调用,如果是外部的工程项目按照java的方式将jar包导入进来. 2.实例化java对象 我们之前学java的时候实例化对象是这个样子的. ...

  2. 从零开始学Kotlin第七课

    1.强制类型转换需要在后面加两个感叹号 2.如果需要在java代码调用kotlin的方法时候使用文件名+kt.方法 3.object 类名 是创建匿名内部类的写法 调用 传入class对象 4.在to ...

  3. 从零开始学Kotlin第四课

    面向对象: //妹子 性格 声音 class Girl(var chactor:String,var voice:String) fun main(args: Array<String>) ...

  4. 从零开始学Kotlin第三课

    kotlin函数和函数式表达式的简化写法: fun main(args:Array<String>) { var result=add(2,5) println(result) ///简化 ...

  5. 从零开始学Kotlin第一课

    Kotlin的方法: 一个简单的计算器: fun main(args:Array<String>){ //主函数main方法 var a=8; var b=9; println(plus( ...

  6. 从零开始学Kotlin第五课

    函数式编程入门: package EL fun main(args: Array<String>) { var names= listOf<String>("tom& ...

  7. 从零开始学Kotlin第二课

    字符串模板 fun main(args:Array<String>){ //主函数main方法 println(diaryGenerater("天安门")); } // ...

  8. 从零开始学Kotlin-枚举(9)

    从零开始学Kotlin基础篇系列文章 枚举的定义 一个类的对象是有限且固定的,这种实例有限且固定的类称为枚举类; 枚举常量用逗号分隔,每个枚举常量都是一个对象; enum class EnumDemo ...

  9. 从零开始学Kotlin-扩展函数(10)

    从零开始学Kotlin基础篇系列文章 什么是扩展函数 扩展函数数是指在一个类上增加一种新的行为,我们甚至没有这个类代码的访问权限: Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 ...

随机推荐

  1. 利用 Settings Sync 同步vs code配置

    vs code上有各种各样不同的插件,如果要在不同的电脑上使用 vs code 配置是件比较麻烦的事情,使用 Settings Sync 将 vs code 配置备份起来,当需要在其他电脑使用  vs ...

  2. Python2.7-decimal

    decimal 模块,提供了对小数精确的计算,内置的 float 类型是以二进制的浮点数保存的,是不准确的,小数点后会有很多奇怪的数字,虽然在一般情况下计算是没问题的,因为近似相等,小数点后十几位才会 ...

  3. “System.Reflection.AmbiguousMatchException”类型的异常在 mscorlib.dll 中发生

    错误提示: “System.Reflection.AmbiguousMatchException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理. 发现不明确的匹配. 问题原 ...

  4. 半导体热阻问题深度解析(Tc,Ta,Tj,Pc)

    半导体热阻问题深度解析(Tc,Ta,Tj,Pc) 本文是将我以前的<有关热阻问题>的文章重新梳理,按更严密的逻辑来讲解. 晶体管(或半导体)的热阻与温度.功耗之间的关系为: Ta=Tj-* ...

  5. iscsi target IET架构

    IET(iSCSI Enterprise Target)是内核态实现的iscsi target,相比于用户态实现的target(比如tgt),iet比较稳定,并且也算是历史悠久,io都直接经过内核态, ...

  6. 十万的License只取决于一个连接

    前段时间看到一份代码,小规模.低难度的一个应用,MVC用到极致,业务逻辑却混成一团麻,应该是中了培训班的毒.现在的程序员,大多是没仔细读过<现代操作系统>,没看过编译原理,不知道堆与栈,没 ...

  7. Luogu P1273 有线电视网

    最近写DP写得比较多了 但是POJ上的题目太傻比了而且不想看英文的题面,然后就在Luogu的试炼场里找了一个DP EX专题写了一下(大概3days吧,一天一题差不多) 这是一道比较简单的DP 话说树形 ...

  8. 欧几里得算法(及扩展)&&快速幂(二分+位运算)

    最近在二中苦逼地上课,天天听数论(当然听不懂) 但是,简单的还是懂一点的 1.欧几里得算法 说得这么高级干什么,gcd入门一个月的人都会吧,还需要BB? 证明可参照其他博客(不会),主要就是gcd(a ...

  9. 牛客OI周赛4-提高组-C-战争[并查集]

    题意 一个长度为 \(n\) 的序列,每个权值互不相同,给出形如 \(l,r,p\) 的信息表示 \([l,r]\) 区间中最小的数是 \(p\) ,问第几个信息开始出现矛盾. \(n\leq 5 \ ...

  10. JavaScript 为什么不要使用 eval

    本文内容 eval 隐藏的 eval 安全问题 结论 参考资料   eval eval 函数是一个高等级的函数,它与任何对象都无关.其参数,如果是一个字符串表达式,那么该函数计算表达式的值:如果是一个 ...