延迟初始化
Kotlin的判空等特性,都是为了保证程序安全所设计的,但是有时这些设计会变得比较麻烦。
对于类中存在很多全局变量,为了保证满足kotlin的空指针检查,不得不写上很多非空判断保护才行。问题解决办法则是,对全局变量进行延迟初始化。
1 | class TestClass{ |
这里lateinit关键字,告诉kotlin我会晚些对这个变量初始化。此时value就不用在一开始就赋值为null,并且也不用做判空处理。
注意:使用lateinit也是有风险的,如果在没有初始化的时候使用它,同样会崩溃。所以使用lateinit的前提是,必须确保在他被任何地方调用之前,已经完成初始化工作。
Android例子:
1 | class MainActivity : AppCompatActivity(), View.OnClickListener{ |
判断变量是否被初始化
1 | class MainActivity : AppCompatActivity(), View.OnClickListener{ |
::adapter.isInitialized可以用来判断变量是否初始化,这个是固定用法。
密封类
密封类关键字:sealed class
场景:
创建一个Result.kt
1 | interface Result |
定义Result接口,Success 和Failure实现这个接口。然后使用:
1 | fun getResultMsg(result:Result){ |
这里的else是必须写的,否则Kotlin认为缺少分支条件无法编译通过,实际上Result的结果只可能是Success和Failure,这里的else完全是为了通过kotlin的检查语法而已。另一个缺点就是,当需要添加新的类UnKnown类,也是实现Result接口,用来实现未知情况的处理,但是忘了修改getResultMsg方法,此时编译器也不会提示,而是进入else。
利用 sealed class
1 | sealed class Result |
密封类是可以被继承的,因此需要添加括号。
此时getResultMsg方法可以写成
1 | fun getResultMsg(result:Result){ |
当when语句传入密封类时,kotlin会自动检查这个密封类有哪些子类,并强制要求每个子类对应的条件全部处理,这样能够保证没有else条件,也不可能出现漏写分支的情况。
扩展函数
扩展函数:即使在不修改某个类的源码的情况下,仍然可以打开这个类,向该类添加新的函数。
语法结构:
1 | fun ClassName.methodName(param1 : Int,param2 : Int):Int{ |
情景:一段字符串可能有字母、数字、特殊符号,我们希望统计其中的字母数量
1 | object StringUtil{ |
这是常见的实现过程,使用扩展函数
1 | fun String.lettersCount():Int{ |
将lettersCount方法定义为String的扩展函数,他就自动带有了String上下文,不再需要接收参数了。定义好扩展函数之后,就可以直接这么用了
1 | val count = "ABC1234#xyz!@#".lettersCount() |
扩展函数能够让API更加简洁,String类是一个final类,任何一个类都不能继承他,但是在kotlin就不一样了,可以向String扩展任何函数,让他的api更加丰富。让编程更加简便。
运算符重载
语法结构:
1 | class Obj{ |
Kotlin的运算符重载允许我们让任意两个对象进行相加
举个栗子:grin:
1 | class Money(val value:Int) |
Money类,默认赋值value
接下来重载实现两个Money相加
1 | class Money(val value:Int){ |
那么我想Money直接和数字相加呢,Kotlin是允许运算符多重重载的。
1 | class Money(val value:Int){ |
附一下运算符的对照表
1 | /* |
扩展函数和运算符重载的应用
这里比较好玩的东西就是这俩结合起来。
让String类型的字符串用上乘法表达式,让他重复n遍。
1 | operator String.times(n:Int):String{ |
使用:
1 | val str = "abc" * 3 |
kotlin也提供了重复n遍的repeat函数,因此可以这么写
1 | operator String.times(n:Int) = repeat(n) |