Kotlin从入门到精通(放弃)一

1,255 阅读7分钟

Kotlin从入门到精通(放弃)一

一.Kotlin的非空校验

在Kotlin中,不允许声明的变量为null,并且它并不像java一样给变量赋了默认值。所以如果你声明了一个未赋值的变量,他就会提示你属性必须初始化或者抽象,或者添加一个延迟初始化,像下图一样:

image.png

所以,如果我们想声明一个未初始化的变量,我们有下面几种方式来声明。

1. 通过可空类型来声明变量

我们在声明变量的时候,在变量类型后面加个?,来赋值为null,这样就可以为变量赋空了,像这样:

var str:String?=null; 我们就可以为str赋一个空了,但是我们在使用的这个变量的时候,又会 出现以下情况

image.png

根据提示,我们也可以看出来,说的是一个可为空的变量不能直接使用,那我们怎么使用这个变量str呢? 一般的也有三个方法:

  • 使用空判断

这个很简单,就是使用变量时,在变量后面添加?.也就是str?.length就可以使用可为空变量了,加这个之后,当变量为空时,就不会执行字符串调用的函数,也就是str为空时,就不会执行str.length这个函数

  • 使用非空断言

这个就是比较刚性,如果str确实为空,会抛一个异常的,不推荐。。。

  • 结合标准函数使用

这个比较常用,一般是和标准函数let结合空操作符:?来使用,比如上面的str字符串,我们使用 var str: String? = null var str1 = str?.let { str.capitalize() } ?: "abc" println(str1)这个代码是什么意思呢,str?.let就是来判空的,如果为空,就不走进这个lambda表达式,然后?:就是空操作符,也就是str为null的话,会给他一个默认值"abc",这里我们需要注意的是,标准库let函数返回值lambda表达式的最后一样,由于let函数中声明的lambda表达式是block:(T)->R,并且返回的是block(this),所以调用的时候T其实就是当前对象,当时是val类型的,也就是java中的final类型,我们并不能改变它的指向,所以我们用str1来接收lambda表达式中返回值。所以str1和str并不相同。结果是这样,但是分析过程不一定对。。。

2. 通过延时初始化关键字来声明

像下面这样声明,就可以使用未初始化的变量了,但是下面的代码使用时,如果未赋值,就会报空指针异常哟。 lateinit var string:String string.capitalize()

小结

Kotlin在声明变量时,如果未赋值,它并不会像java一样会给个默认值,所以不允许我们调用未赋值的变量,如果我们想自己拿回主动权,通常可以通过可空变量?和延迟初始化关键字lateinit,但是这个也会代码在执行的时候可能抛空指针异常,这个呢,就需要我们自己在代码里面处理了,像可以通过可空判断?.、非空断言!!以及标准库函数?.let来主动处理,避免空指针异常。

二.Kotlin中的函数

1. 标准的函数

  • 可以指定参数,这样就省去了函数的重载
  • 方法中可以给临时变量设置默认值
  • 可以在声明函数时,用等于号给函数赋默认值

举个例子: fun funCommon(str: String, i: Int = 12): String { return str; } 像上面声明一个函数,形参i,我们可以给默认值,我们在调用时,可以具名形参(省去了重载),像这样 funCommon(str = "abc", i = 16) funCommon(str = "abc")来调用

2. 匿名函数

  • 常规的匿名函数
  • 类型推断的匿名函数
  • 使用inline避免lambda对象的重复创建
  • 匿名函数的写法

什么是匿名函数?匿名函数就是没有名字的函数,我们一般是通过以下步骤来给匿名函数定义: 1.声明一个函数变量用来接收匿名函数; 2.用lambda表示函数参数以及返回值,可以省去 形参声明的变量,也就是i:Int可以直接替换成Int作为形参声明; 3.={}为lambda表达式赋值;4.lambda表达式的最后一行是匿名函数的返回值。

  • 常规写法及推断类型写法

举个例子,我们定义一个匿名函数 var funAnonymous: (Int) -> String = { it.toString() },我们分析下这个函数,它是个函数变量名为funAnonymous,它是一个入参为Int类型,这里面我们省略了形参变量名编写,返回值为String类型的函数,函数体的内容为大框号里面的内容,由于只有一个参数所以it代表了入参的变量,它的返回值即代码的最后一行。上面只是一个单个参数的,当多个参数时,我们需要定义多个参数来接收入参,像var funAnonymous5: (Int, String) -> String = { _i, _s -> _i.toString() + _s } 这样在大框号中定义接收或 var funAnonymous4: (_i: Int, _s: String) -> String = { _i, _s -> _i.toString() + _s }这样在入参的时候声明下类型都可以。 其实我们也可以省去中间入参和返回值的代码编写,因为Kotlin具有类型推断的能力,所以 var funAnonymous: (Int) -> String = { it.toString() }可以写成 var funAnonymous = { it.toString() },这样看上去像是一个代码块,但是在Kotlin中,它的确是个函数!类似的还有funAnonymous4还可以这样定义, var funAnonymous4 = { _i, _s -> _i.toString() + _s }这个也是省去了形参和返回值,直接类型推断的写法。

3.高阶函数与inline关键字以及闭包的概念

  • 高阶函数的概念和写法

什么是高阶函数?听上去好像很厉害的样子,你用java的理解就是方法的参数是一个匿名对象,或者如果你玩过C或者C++就一定知道函数指针的概念,形参就是一个函数指针类似这样的概念。

举个例子说吧fun funSenior(str:String,param:(String)->String):String{ return param(str) }这个就是一个高阶函数,函数的参数是一个匿名函数,那么,我们怎么调用呢,我们来定义一个匿名函数param: var param={ str:String-> "$str abc" }然后我们来调用这个高阶函数funSenior("abc", param)

  • 闭包的写法和作用

注意,我们在funSenior这个高阶函数中使用到了闭包,那什么是闭包呢,按照java的理解,方法都是压栈的,方法中的变量随着方法的执行结束变量生命周期就结束了,这里我们在funSenior的第一个参数str作为funSenior的另一个参数匿名函数的参数,像这样被定义的函数(param)可以访问定义其的函数(senior)声明的变量(str),就形成了闭包,闭包主要是解决脚本语言没有包的概念,防止同名变量的问题。需要注意的是,我们可以这样去调用这个高阶函数funSenior("12"){ "$it abc" }当然,这样写是有前提的,只有当最后一个参数是lambda表达式或者只有一个匿名函数参数。

  • 内联关键字

虽然lambda很好用,但是它也有弊端,jvm会为每个lambda表达式创建一个对象,调用完后又回去销毁,那inline就是为了避免对象的创建,它有点类似于C中的宏定义,当你在调用lambda表达式的函数时,它就不会直接创建对象,编译器直接将函数赋值粘贴过去,就拿上面的高阶函数的调用来说,我们没加inline前,反编译的源码是这样的 funSenior("12", (Function1)null.INSTANCE);,加了inline之后(new StringBuilder()).append(str$iv).append(" ABC").toString(); 可以看出来,使用inline之后,代码更多了,但是并没有创建Function1对象,这样就避免了对象的重复创建。

  • 函数引用

对了还有个关于函数的知识点,就是将函数的引用作为参数,类似于这样的写法: /** * 函数引用作为参数 */ funSenior("123", ::param1); } fun param1(str: String): String { return str; } fun funSenior(str:String,param:(String)->String):String{ return param(str) },怎么理解这样的写法呢,你可以用C中的函数指针或者java中的对象的方法去理解它,其实它本身就是一个函数对象,并且在使用lambda表达式的地方都可以使用函数应用,额,对了,在java8中好像也可以用这样的写法。

4. 构造函数

这个其实没有什么好说的,需要记住的就是它的执行顺序,主构造->属性->init代码块->次构造函数,所以注意调用的时序哟。

小结

Kotlin可以说是函数式编程,并且依靠强大的lambda表达式和类型推断大大简化了代码量,但是它也有弊端,所以使用了inline关键字,并且类似于脚本语言,它也有闭包的概念,同样它又有点像C语言那样可以将函数对象(引用)作为参数,总之,理解了kotlin的函数式编程,基本上就可以看懂别人的Kotlin小项目。码字不易,如果觉得此文对你有用,欢迎动动小手交流交流。