Kotlin 学习笔记- 空类型和智能类型转换

简介: Kotlin 学习笔记聚焦于空类型和智能类型转换,深入解析非空与可空类型、安全调用操作符、Elvis 运算符、非空断言运算符及智能类型转换等内容,助你高效掌握 Kotlin 语言特性,避免 NullPointException 异常,提升代码质量。

简介: Kotlin 学习笔记- 空类型和智能类型转换
本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

imgKotlin - 空类型和智能类型转换

空类型

Kotlin 跟 Java 的最大不同应当就属空类型这点了,使用 Kotlin 开发,IDE 会智能的对可能为空的地方进行报错提示,开发者必须处理该错误,否则连编译都通过不了,从而降低程序 NullPointException 异常的出现几率,所以,一般情况下使用 Kotlin 开发很少见到 NPE 异常。

非空与可空类型

fun getName(): String {
return "lqr"
}
这是一个很普通的函数声明,它指明了函数返回值是一个 String 类型,对此,Kotlin 会认为这是一个不可能返回 null 结果的函数,那如果我就是要返回 null 会怎样?

// IDE报错:Null can not be a value of a non-null type String
fun getName(): String {
return null
}
该 IDE 的报错提示说明了函数返回值类型 String 是一个不能为 null 的值,即非空类型。如果需要函数可以返回 null 的话,需要对函数返回值类型做一点小修改,使其可以用空,这仅仅只需要在返回值类型后面追加 ? 即可:

fun getName(): String? {
return null
}
综上,Kotlin 的类型声明分为两类(包括但不限于函数返回值类型),分别是:

非空类型:单纯声明的类就是非空类型,如:String。
可空类型:通过在类后面放置 ? 来声明,如:String?。

可空类型操作符

Kotlin 为保证代码空安全,提供了几种处理方式,本节主要陈述其中的 3 种操作符。

了解更多 Kotlin 空安全知识,请访问:https://www.kotlincn.net/docs/reference/null-safety.html(opens new window)

安全调用操作符(?.)

以获取字符串长度为例,非空类型变量直接通过 .length 即可:

val name: String = "lqr"
println(name.length)
而可空类型变量,不仅需要在类型声明时使用 ? ,在调用可空类型对象的成员变量 length 时也需要使用 ?. 进行处理:

val name: String? = null
println(name?.length) // 输出:null
因为 name 的值为 null,所以 .length 并不会被执行,因此这代码相当于 println(null),虽然结果是 null,但程序并不会崩溃。

安全调用操作符(?.):如果接收者非空,就调用一个方法或访问一个属性,否则不执行。

Elvis 运算符(?:)

日常开发中,我们经常习惯于用一行代码同时处理变量非空或为空的情况,在 Kotlin 中,借助 if-else 代码可以这么写:

println(if (getName() != null) getName() else "Default Name")
Java 有三元运算符,而 Kotlin 没有,所以这里只能用 if-else。

Kotlin 还提供了 Elvis 运算符(?:),可以对 if-else 进行简化:

println(getName() ?: "Default Name")
Elvis 运算符(?:):如果 ?: 左侧表达式非空,elvis 操作符就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧为空时,才会对右侧表达式求值。

非空断言运算符(!!)

如果你非常非常确定变量的值绝对不可能为 null,那么你可以在对象调用时使用 !! 对其进行转换成非空类型:

val value: String? = "Hello LQR"
println(value!!.length)
非空断言运算符(!!):将任何值转换为非空类型,若该值为空则抛出异常。

智能类型转换

类型转换在开发中很常见,特别是在多态的应用情景里,会使用父类变量接收子类对象,并且可能会需要强转成具体的子类类型以使用特定的子类功能。

open class Parent {}

class Child : Parent() {
fun getName(): String {
return "lqr"
}
}

fun main(args: Array) {
val man: Parent = Child()
println((man as Child).getName())
}
Kotlin 里,强转需要使用 as 关键字来处理。

当然了,这里的代码处理的很不好,通常在转换前会先判断对象的具体类型后再做强转,以免出现类型转换异常,因此,代码可以修改为:

val man: Parent = Child()
if (man is Child) {
println(man.getName())
}
Kotlin 中使用 is 关键字来判断变量类型,if 代码块中,man 变量已经被识别为 Child 类型了,因此不再需要显式强转,这就是 Kotlin 的智能类型转换,反观 Java 就显的有些笨笨的了:

Parent man = new Child();
if (man instanceof Child) {

System.out.println(((Child) man).getName());

}
回过头再来看看 as 关键字,Kotlin 代码中使用 as 进行对象的类型强转,如果我们不先进行类型判断,就直接强制变量类型,一旦被强转的对象类型有误,就必定会抛出ClassCastException:

// Exception in thread "main" java.lang.ClassCastException: com.charylin.kotlinlearn.Parent cannot be cast to com.charylin.kotlinlearn.Child
val parent: Parent = Parent()
val child: Child = parent as Child
print(child)
还好,Kotlin 的智能类型转换功能为 "直接强转党" 提供了一条出路,那就是使用 as?,同时如果变量有显式指定类型的话,需要将其改为可空类型,或者干脆把变量类型声明去掉:

val parent: Parent = Parent()
val child: Child? = parent as? Child
// val child = parent as? Child // 这种写法也是OK的
print(child) // 输出:null
as? 相比 as 要智能一些,当强制类型有误会时,结果会为 null。

相关文章
|
8天前
|
安全 IDE Java
Kotlin教程笔记(3) - 空类型和智能类型转换
Kotlin教程笔记(3) - 空类型和智能类型转换
|
15天前
|
安全 IDE Java
Kotlin教程笔记(3)- 空类型和智能类型转换
本教程详细讲解了Kotlin中的空类型、非空与可空类型、可空类型操作符、安全调用操作符、Elvis运算符、非空断言运算符以及智能类型转换等内容,帮助开发者更好地理解和使用Kotlin的空安全机制。适合希望深入了解Kotlin语法的开发者。快速入门请参考“简洁”系列教程。
26 3
|
23天前
|
安全 IDE Java
Kotlin教程笔记(3) - 空类型和智能类型转换
Kotlin教程笔记(3) - 空类型和智能类型转换
|
1月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
20 1
|
2月前
|
Android开发 开发者 Kotlin
告别AsyncTask:一招教你用Kotlin协程重构Android应用,流畅度飙升的秘密武器
【9月更文挑战第13天】随着Android应用复杂度的增加,有效管理异步任务成为关键。Kotlin协程提供了一种优雅的并发操作处理方式,使异步编程更简单直观。本文通过具体示例介绍如何使用Kotlin协程优化Android应用性能,包括网络数据加载和UI更新。首先需在`build.gradle`中添加coroutines依赖。接着,通过定义挂起函数执行网络请求,并在`ViewModel`中使用`viewModelScope`启动协程,结合`Dispatchers.Main`更新UI,避免内存泄漏。使用协程不仅简化代码,还提升了程序健壮性。
76 1
|
3月前
|
调度 Android开发 开发者
【颠覆传统!】Kotlin协程魔法:解锁Android应用极速体验,带你领略多线程优化的无限魅力!
【8月更文挑战第12天】多线程对现代Android应用至关重要,能显著提升性能与体验。本文探讨Kotlin中的高效多线程实践。首先,理解主线程(UI线程)的角色,避免阻塞它。Kotlin协程作为轻量级线程,简化异步编程。示例展示了如何使用`kotlinx.coroutines`库创建协程,执行后台任务而不影响UI。此外,通过协程与Retrofit结合,实现了网络数据的异步加载,并安全地更新UI。协程不仅提高代码可读性,还能确保程序高效运行,不阻塞主线程,是构建高性能Android应用的关键。
61 4
|
4月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
**Kotlin中的`by lazy`和`lateinit`都是延迟初始化技术。`by lazy`用于只读属性,线程安全,首次访问时初始化;`lateinit`用于可变属性,需手动初始化,非线程安全。`by lazy`支持线程安全模式选择,而`lateinit`适用于构造函数后初始化。选择依赖于属性特性和使用场景。**
151 5
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
|
4月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
59 8
|
4月前
|
安全 Java Android开发
探索Android应用开发中的Kotlin语言
【7月更文挑战第19天】在移动应用开发的浩瀚宇宙中,Kotlin这颗新星以其简洁、安全与现代化的特性,正迅速在Android开发者之间获得青睐。从基本的语法结构到高级的编程技巧,本文将引导读者穿梭于Kotlin的世界,揭示其如何优化Android应用的开发流程并提升代码的可读性与维护性。我们将一起探究Kotlin的核心概念,包括它的数据类型、类和接口、可见性修饰符以及高阶函数等特性,并了解这些特性是如何在实际项目中得以应用的。无论你是刚入门的新手还是寻求进阶的开发者,这篇文章都将为你提供有价值的见解和实践指导。
|
4月前
|
SQL 安全 Java
Android经典面试题之Kotlin中object关键字实现的是什么类型的单例模式?原理是什么?怎么实现双重检验锁单例模式?
Kotlin 单例模式概览 在 Kotlin 中,`object` 关键字轻松实现单例,提供线程安全的“饿汉式”单例。例如: 要延迟初始化,可使用 `companion object` 和 `lazy` 委托: 对于参数化的线程安全单例,结合 `@Volatile` 和 `synchronized`
59 6