标准函数let
首先,最常用的?.
操作符,是判空操作符,当对象不为空时正常运行,当对象为空时,什么都不做。
当我们不需要kotlin的空指针检查时,我们需要在我们的对象后加!!
同时,我们也可以使用kotlin的标准函数let
fun doEat(p:Person?) {
p?.eat()
}
fun doEat(p: Person?) {
if (p != null) {
p.eat()
}
}
fun doEat(p: Person?) {
p?.let {
it.eat()
}
}
以上三种写法,可以对可以为空的Person对象判空。let
函数可以处理全局变量的判空问题。
标准函数with
val listX= listOf("A","B","C","d","e")
val builder=StringBuilder()
builder.append("ok\n")
for(letter in listX){
builder.append(letter).append("\n")
}
builder.append("fine")
val resultX=builder.toString()
println(resultX)
此时,调用with
函数,帮助在连续调用同一个对象的多个方法时,让代码更精简。
val listY= listOf("a","b","c","D","E")
val resultY= with(StringBuilder()){
append("ok")
for(fruit in listY){
append(fruit)
}
append("fine")
toString()
}
上面kotlin代码的含义是,首先给with
传入第一个参数是StringBuilder()
对象,之后的lambda表达式使用的都是这个对象。而lambda表达式的最后一行作为with的返回值返回。
标准函数run
同样是上面的例子,可以使用run
来实现
val listZ= listOf("a","b","c","D","E")
val resultZ=StringBuilder().run {
append("ok")
for(fruit in listZ){
append(fruit)
}
append("fine")
toString()
}
run
和with
区别不大,with
需要将对象作为参数传入,run
无需传参数,直接跟在对象之后
标准函数apply
与run
with
不同的是,apply
返回一个加工之后的对象,无法直接指定返回值
val listZ= listOf("a","b","c","D","E")
val resultZ=StringBuilder().apply {
append("ok")
for(fruit in listZ){
append(fruit)
}
append("fine")
}
println(resultZ.toString())
静态方法
静态方法可以不用创建实例就可以调用。在java中直接加static
关键字即可。
例如自定义一个工具类,若使用java实现,那么需要将类中的方法设置为静态的。
在kotlin中,只需要用单例类的写法,如下。
object Utils {
fun doAction3(){
println("action3")
}
}
此时,虽然doAction3
并非静态方法,但在调用时,仍然可以Utils.doAction3
。
这样的缺点是,在单例类中的所有方法都是“静态”的。
另一种方式是这样的:
class Util {
companion object{
fun doAction1(){
println("action2")
}
}
fun doAction2(){
println("action1")
}
}
使用了companion object
关键字
doAction1
并非静态方法,而是companion object
关键字在这个类中创建了一个伴生类,doAction1
则是这个伴生类中的实例方法。调用Utils.doAction1
实际上就是调用伴生类中的方法。
以上两种方式并非真正的静态方法。加注解可以使他们变成真正的静态方法。
class Util {
companion object{
@JvmStatic
fun doAction1(){
println("action1")
}
}
fun doAction2(){
println("action2")
}
}
@JvmStatic
该注解仅仅能在companion object
代码块中的方法上添加。
加注解之后,该方法成为了真正的静态方法,在java和kotlin中可用静态方法的方式来调用。
顶层方法
顶层方法是指没有定义在任何类中的方法。kotlin默认将所有的顶层方法都编译为静态方法。
创建一个kotlin File/Class ,里面创建的任何方法都是顶层方法(因为这些方法并不存在于任何类中)
这些方法在项目任何位置都可以直接调用,无需创建实例,只需键入方法名。
实际上,比如创建的kotlin文件名为Helper
,kotlin编译器自动生成了一个HelperKt
的java类,文件中所有的方法,都以静态的形式存在于该HelperKt
的java类,若要在java中使用这些方法,直接静态调用:
HelperKt.play();
延迟初始化
我们在创建全局变量时,常初始化赋值为null,但我们为了满足kotlin的要求,必须要做大量的判空处理。
为了解决这一问题,我们常常使用延迟初始化
private lateinit var adapter: Adapter
我们在延迟初始化变量后,在使用该变量之前一定要保证变量被赋值
可以通过代码来判断一个全局变量是否已经完成了初始化,有效避免了重复初始化。
if(!::adapter.isInitialized){
adapter=xxxxxx
}
密封类
密封类使用了sealed
关键字
sealed class Result
class Success(val msg:String):Result()
class Failure(val error:Exception):Result()
fun getResultMsg(result: Result)=when(result){
is Success->result.msg
is Failure->result.error.message
}
Success
和Failure
两个类都继承自Result
类,若Result
并非密封类,那么在getResultMsg
方法中需要 else 写出其他条件的操作
而Result
是密封类,kotlin编译器自动检查该密封类有哪些子类,强制要求将他的子类全部处理,保证了即使没有else条件,也不会漏写条件分支。
扩展函数
扩展函数即不修改某个类的源码的情况下,仍然可以打开该类,向该类中添加新的函数。
例如现在的需求是统计某字符串中字母的个数:
fun String.letterCount():Int{
var count=0
for(char in this){
if(char.isLetter()){
count++
}
}
return count
}
此处的this
表示字符串本身,自定义该函数之后,意味着String类中添加了这样一个方法。
运算符重载
运算符重载可以允许我们将任意两个对象相加。
class Money(val value:Int){
operator fun plus(money: Money):Money{
val sum=value+money.value
return Money(sum)
}
operator fun plus(newValue:Int):Money{
val sum=newValue+value
return Money(sum)
}
}
使用operator
关键字来修饰函数表示允许Money对象相加。
operator
关键字可以多次修饰一个方法,多次重载。
至此,Money对象可以与Money对象相加,也可以与整型数字直接相加