前言:不走心的努力,都是敷衍你自己。你要坚信现在所有过不去的坎,将来总有一天会当笑话说出来。

一、概述

  Java 中定义一个变量可以默认不赋值,因为系统默认会赋值一个默认值,并且可以定义一个为 null 的变量,这样在使用的时候就需要判断该变量是否为 null 。从代码的简洁性以及代码的可读性来说,Kotlin 能更好处理,那么 Kotlin 定义一个可为 null 的变量怎么定义呢?

Kotlin 的空安全设计对于声明可为空的参数,在使用时需要做空判断处理,有两种处理方式,一是字段后面加!!符号,像 Java 那样抛出空异常,二是字段后面加?符号可不做处理返回值为null或者配合?:做空判断处理。下面针对 Kotlin 这一特性来详细讲解。

二、可空类型:?

在变量类型后面加?符号,表示该变量为可空类型;类型后面没有加?符号,表示该变量为不可空类型。可空变量在使用时需要做空判断处理,不可空类型变量则不需做空判断处理,因为没有意义。

定义一个可空变量的格式:

修饰符  变量名:类型? =  初始值

不可空变量:

修饰符  变量名:类型 =  初始值

因为类中的变量必须要初始化值:

  • 1.如果没有声明类型,系统会自动识别你赋值的类型,如果赋值为null,则系统自动声明为Nothing?,该变量值只能为null,不能重新赋值为其他类型值;
  • 2.如果声明类型,但是类型后没有?,则表示该变量不可以为null,在使用时不需要做空判断;
  • 3.如果声明类型,类型后加?,表示该变量可为null,如:Int?,在使用时需要做空判断。
//1.不可空类型并且初始化值为null
    var numA = null //没有声明类型,初始化值为null,不能重新赋值其他类型值
    //numA = 10     报错
    //numA = "文字" 报错

	//2.不可空类型并且初始化值不为null
    var numB: Int = 15
    //numB = null  报错,类型没有‘?’,不能赋值为null
    numB = 0 //可以赋值Int类型的值
    if (numB != null) {//numB声明不为null,这个判断没有意义
    }

	//3.可空类型
    var numC: String? = null//该变量可以为String类型的值和 null
    numC = "HelloWord"
    if (numC != null) {//可空类型,使用时需要做空判断处理
    }

我们看下方法参数和返回值可空和不可空的使用:

fun setType(type: Int?) {//表示参数type可以为空
    }

    fun setType(type: Int) {//表示参数type不可以为空
    }

    fun getName(): String? {//表示返回的值可以为空
        return null
    }

    fun getName(): String {//表示返回的值不可以为空
        return "名字"
    }

三、安全调用运算符:?.

安全调用运算符?.用于可空类型变量安全调用非空类型的属性或者函数,而不会抛出空指针异常,当变量为null时,直接返回空值null,否则进行调用并返回结果。

3.2 if…else

在上面我们提到可空类型的使用是需要判断的,Kotlin 中可以if…else来判断是否为空:

var user: User? = null

    if (user == null) {

    } else {

    }

3.2 安全调用运算符

也可以使用安全运算符?.来判断是否为空,如果可空类型变量为null时怎返回null,格式如下:

可空类型变量?.属性/方法名

表示当变量user为null时,依然执行user?.name语句,不会报错,返回一个null值。

var user: User? = null
    //user为null,获取返回的数据都为null
    Log.e(TAG, "安全调用运算符:user == name:${user?.name} | age: ${user?.age}| sex: ${user?.sex}")

打印数据如下:

安全调用运算符:user == name:null | age: null| sex: null

3.3 链式调用

链式调用能大量避免空指针异常NullPointException,因为链式整只有一个为null,则整个链式返回值就为null

var user: User? = null
	var address = user?.info?.address//链式调用获取user里面的info的address
	Log.e(TAG, "安全调用运算符:address == $address")

打印数据如下:

安全调用运算符:address == null

3.4 let函数

使用let函数,避免写一些判断null的操作,当你需要定义一个变量在一个特定的作用域范围内,let函数更适合,它实际是一个作用域函数。使用格式如下:

//判断object为null的操作
	object?.let{//表示object不为null的条件下,才会去执行let函数体
   		it.todo()
   		···
	}

在代码块内可以通过it替代该对象,即it就是user:

var user: User? = null
    user?.let {//it表示user,如果user为null,let函数不会被执行
        var name = it.name
        var age = it.age
        var sex = it.sex
    }

当然其他作用域函数with,run,apply,also等也适用

四、Elvis运算符(空值合并运算符):?:

?:是一个二元运算符,作用是判断可空类型时空值合并,语法为:可空类型数据 ?: 空值合并到的数据,Kotlin 中不存在三目运算符。当数据非空时,直接返回数据,当数据为空时,返回合并到的数据。利用该运算,能很好把可空类型转为非空类型。

判断一个可空类型时,如果返回数据不为空时,则返回当前数据;如果返回数据为空时,则返回一个设定好的默认值。

var length = 0
    var strA: String? = "HelloWord"

	//传统判断
    length = if (strA != null) strA.length else -1
    Log.e(TAG, "空值合并运算符 strA不为null:length == $length")
    
    //空值合并运算符?:
    strA = null
    length = strA?.length ?: -1//如果strA?.length为null则返回-1,否则返回strA?.length
    Log.e(TAG, "空值合并运算符 strA为null:length == $length")

其实if (strA != null) strA.length else -1 等价于 strA?.length ?: -1,打印数据如下:

空值合并运算符 strA不为null:length == 9
空值合并运算符 strA为null:length == -1

五、非空断言运算符:!!

非空断言运算符用于断言一个可空各类型变量,如果该变量为null,则会抛出空指针异常kotlin.KotlinNullPointerException!!方便爱好抛出空指针异常的开发者使用,在一个可空变量后面加上!!,如果变量为null,运行时会抛出空指针异常。

//1.变量不为`null`
    var name: String? = "HelloWord"
    Log.e(TAG, "非空断言运算符:length == ${name!!.length}")

	//2.变量为`null`
    name = null
    val user = User(name!!, 10, "性别")//运行异常,抛出空指针异常
    Log.e(TAG, "非空断言运算符:length == ${user?.name}")

可以看到当name != null时,正常运行:

非空断言运算符:length == 9

name == null时,name!!抛出空指针异常。

java可空参数 java可空类型_Kotlin as?


在最新版的 Kotlin 中,如果检查了变量非空,则直接调用非空类型的属性和函数时不需要!!

六、安全转换运算符:as?

在 Kotlin 中使用as进行类型的强制转换,as?表示类型的安全转换。如果as进行类型转换不能正常转换的话会抛出类型转换异常ClassCastException,而as?则会返回 null,不会抛出异常。

6.1 as的使用

var num: Int? = "Kotlin" as Int//抛出类型转换异常

String类型无法转换成Int类型,as进行类型转换是不能正常转换,抛出异常。

java可空参数 java可空类型_Kotlin非空断言_02

6.2 as?的使用

使用as?进行安全转换,当需要转换的数据为null时,返回null,不抛出异常;当需要转换的类型不一致时,也是返回null,不抛出异常。

var str: String? = null
    var num = str as? Int
    Log.e(TAG, "安全转换运算符:num == $num")

    str = "Android"
    num = str as? Int
    Log.e(TAG, "安全转换运算符:num == $num")

打印数据如下:

安全转换运算符:num == null
安全转换运算符:num == null

总结一下上面几种运算符:

运算符

符号

含义

例子

可空类型

?

在变量类型后面加?符号,表示该变量为可空类型。

var numC: String? = null

安全调用运算符

?.

可空类型变量安全调用非空类型的属性或者函数,而不会抛出空指针异常,

当变量为null时,直接返回空值null

var name = user?.name

空值合并运算符

?:

判断可空类型,如果数据为null则空值合并。

var length = strA?.length ?: -1

非空断言运算符

!!

断言一个可空各类型变量,如果该变量为null,则会抛出空指针异常。

val user = User(name!!, 10, "性别")

安全转换运算符

as?

类型安全转换,转换不成功则返回null,不抛出异常。

var num = str as? Int

源码地址:https://github.com/FollowExcellence/KotlinDemo-master

点关注,不迷路