Kotlin知识归纳(六) —— 类型系统

前序

      Kotlin引入可空性的新特性,旨在消除来自代码空引用的危险。将运行时的NPE转变成编译器的错误。

可空类型与非空类型

      在Kotlin类型系统中,分为可空类型和非空类型。当你允许一个变量为null时,需要显示在类型后面加上一个问号,将其非空类型转换为可空类型。

      常见的类型都是非空类型,不能存储null引用,只有在类型后面添加个问号转换为可空类型后,变量才可存储null引用。

val str:String? = null

      对于一个可空类型的值,不能直接调用该类型的方法,也不能把他赋值给非空类型,更不能把它传递给接受非空类型参数的函数。可空类型看似和非空类型并没有什么交互性,但其实并不是,只是需要对可空类型进行一个判空后,才能正常交互:

val str:String? = “”
if (str != null)
    str.length

      一旦对可空类型的对象进行判空,编译器就会对判空的作用域内把该对象当作非空对待。

Kotlin的可空性与Java的Optional

      Java8中引入的特殊包装类型Optional来解决null引用问题。但这种方法使代码更加冗长,并且额外的包装类还影响运行时的性能,因此并没有被广泛使用起来。

      但在Kotlin中,可空和非空的对象在运行时没有什么区别,可空类型并不是非空类型的包装类。所有的检查都是编译器完成,这使得Kotlin的可空类型并不会在运行时带来额外的开销。

安全调用运算符:?.

      Kotlin标准库中有一个高效的安全调度运算符:?. 。它将null检查和调用合并成一个操作。当你使用?.调用一个可空类型对象的方法时,若值不为空,则方法会被正常执行;若值为null,则方法调用不发生,并整个表达式返回null。

image

安全调用除了可以调用方法,还可以用来访问属性。

Elvis运算符:?:

      Elvis运算符?:用来提供替代null的默认值。Elvis运算符接收两个表达式,如果左侧表达式非空,则返回其左侧表达式。当左侧表达式为空,则返回右侧表达式。

Elvis运算符经常与安全调度运算符一起使用:

val str:String? = null
println(str?.length ?: 0)

Elvis运算符也可以配合return 和 throw一起使用,当运算符左边为null时,能提前返回函数或抛出异常。

val str:String? = null
//为空抛一次
val length = str?.length ?: throw IllegalArgumentException()
println(length)
str?.let {
    println(length)
} ?: return

//等价于
if(str == null)
    //函数类型为空时直接打断函数继续执行
    return
    
//str不为null,则继续执行。
println(length)

也可以配合run函数配合使用,替代if-lese:

str?.let { 
    //str不为空的逻辑
} ?: run { 
    //str为空时逻辑
}

非空断言:!!

      Kotlin为NPE爱好者提供非空断言运算符 !! (双感叹号),可以把任何对象转换成非空类型,从而调用该对象方法,但可能造成抛出NPE。

val str:String? = null
//抛NPE
println(str!!.length)

      所以只有确保该可空类型对象不为空时,才使用非空断言。当使用非空断言而且发生异常时,异常栈只表明异常发生在哪一行,并不会指明哪个表达式,所以最好避免同一行中使用非空断言。

安全转换:as?

      和常规的Java转换一样,当被转换的值不是你视图转换的类型时,会抛出ClassCastException异常。一般解决方案是在使用在转换前使用is检查来确定该值是否符合转换类型。但Kotlin提供更简洁的运算符——安全转换运算符:as?

//定义父类和子类
open class Animal{
    fun getName(){
    }
}
class Dog:Animal(){
    fun getDogName(){
    }
}

fun main(args:Array<String>){
    val animal:Animal = Dog()
    val dog = animal as? Dog ?: return
    dog.getDogName()
}

安全转换运算符尝试将值转换成给定的类型,否则返回null:

image

let函数

      let函数将调用它的对象变成lambda表达式的参数。配合安全调度运算符可以把调用let函数的可空对象,转变成非空类型。然后在let函数中调用一系列对该可空类型的操作。

fun main(args:Array<String>){
    val str:String? = null
    str?.let {
        daqi(it)
    }
}

fun daqi(str:String){

}

      当需要检查多个值是否为null时,不建议使用嵌套的let调用来处理,建议使用一个if语句对这些值进行一次性检查。

可空类型的扩展

      对可空类型的进行扩展的好处是,允许接收者为null时调用扩展函数,并在扩展函数中处理null,而不用确保变量不为null后再调用该对象的方法。因为当实例为null时,成员方法永远不会被执行。

      Kotlin标准库中的CharSequence存在两个扩展函数:isNullOrEmpty和isNullOrBlank,可以由String?类型的接收者调用。

      对可空类型定义扩展函数时,意味着函数体中的this可能为空,需要做对应的空处理。

fun String?.daqi(){
    if (this == null){
        println("this is null")
    }
}

fun main(args:Array<String>){
    val str:String? = null
    由于接收的是可空类型,不需要使用?.
    str.daqi()
}

延迟初始化

      Kotlin中,属性声明为非空类型时,必须在构造函数中初始化。但属性可以在一个特殊的方法中,通过依赖注入来初始化。这时不能在构造函数中为属性提供一个非空初始化器,但你仍想将该类型声明为非空类型,避免空检查。可以使用lateinit关键字修饰该变量,请将该变量使用var修饰,因为val必须会编译成必须在构造方法中初始化的final字段。

class daqi{
    private lateinit var name:String
    
    fun onCreate(){
        name = "daqi"
    }
}

可空性与Java

      Kotlin会根据Java中的可空性注解,来对来自Java的类型分为可空类型和非空类型。如,@Nullable注解的对象,会被Kotlin当作可空类型的对象。@Notnull注解的对象,会被Kotlin当作非空类型的对象。

      当可空性注解不存在时,Java类型会被转换为Kotlin的平台类型。平台类型本质上是Kotlin不知道其可空信息,既可以把它当作可空类型,也可以把它当作非空类型。如果选择非空类型,编译器会在赋值时触发一个断言,防止Kotlin的非空变量保存空值。这意味着需要开发者负责正确处理来自Java的值。

      Kotlin定义的函数中,编译器会生成对每个非空类型的参数的检查,如果使用不正确的参数调用,会立即抛出异常。(这种检查在函数调用的时候就被执行了,而不是等到该异常参数被使用时才执行。)

基本数据类型

      Java区分基本数据类型和引用类型,基本数据类型具有高效存储和传递的性质。当你需要在泛型类中存储一些基本数据类型时,需要以基本数据类型的包装类型进行存储。因为JVM不支持用基本数据类型作为类型参数。

      Kotlin并不区分基本类型和包装类型。对于变量、属性和返回类型,Kotlin的基本数据类型会被编译成Java的基础数据类型。只有对于泛型类时,才会被编译器成对应的Java基本类型包装类。

基本数据类型

      当使用Java声明的基本数据类型变量时,该类型会变成非空类型,而不是平台类型。因为Java的基本数据类型不能存储null值。

      Kotlin中可空的基本数据类型会被编译成对应的包装类型,因为Java的基本数据类型不能存储null值。

数字转换

      kotlin不会自动把数字从一种类型转换成另一种取值范围更大的类型。Kotlin为每种基本数据类型(Boolean除外)都定义了转换到其他基本数据类型的函数。

      Kotlin要求转换必须显式的,因为在Java中,比较装箱值时,不仅检查他们存储的值,还会比较装箱类型。

//此处比较会返回false
new Integer(42).equals(new Long(42))

      Kotlin标准库为字符串也提供了转换为基本数据类型的扩展函数。如果对字符串解析失败,则抛出NumberFormatException()方法。

根类型

      Any类型是所有Kotlin非空类型的超类。但Any不能持有null值,当需要持有任何值的变量包括null值,必须使用Any?

      Any只包含toString、equals和hashCode。所有Kotlin的这些方法都是从Any中继承来得。但Any不能使用使用其他Object的方法(如:wait和notify)

类型参数的可空性

      Kotlin中所以泛型类和泛型函数的类型参数默认都是可空的,因为默认上界是Any?

      如果需要类型参数非空,则必须为其指定一个非空的上界:

fun <T:Any> daqi(t:T){
    
}

参考资料:

android Kotlin系列:

Kotlin知识归纳(一) —— 基础语法

Kotlin知识归纳(二) —— 让函数更好调用

Kotlin知识归纳(三) —— 顶层成员与扩展

Kotlin知识归纳(四) —— 接口和类

Kotlin知识归纳(五) —— Lambda

Kotlin知识归纳(六) —— 类型系统

Kotlin知识归纳(七) —— 集合

Kotlin知识归纳(八) —— 序列

Kotlin知识归纳(九) —— 约定

Kotlin知识归纳(十) —— 委托

Kotlin知识归纳(十一) —— 高阶函数

Kotlin知识归纳(十二) —— 泛型

Kotlin知识归纳(十三) —— 注解

Kotlin知识归纳(十四) —— 反射

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352