kotlin中的Unit和Nothing 关键字
让我们先从 kotlin 的类型继承关系开始:众所周知,kotlin 中所有东西都有类型,对象、函数等等,就连 Unit,Nothing 也有对应的类型。我们来看一下kotlin 中的类型层次结构。
从顶部开始
所有类型的 Kotlin 对象都被组织成子类型/超类型关系的层次结构。该层次结构的“顶部”是抽象类Any。例如,String 和 Int 类型都是的子类型Any。
graph BT
String-->Any
Int-->Any
这里的 Any 相当于 java 中的 Object,同样的,如果我们声明一个类,没有显式指定继承自哪个类,那这个类就是 Any 的直接子类。
如果该类指定了父类,那么 Any 会是该类的最终父类(祖先)
1 |
|
graph BT
Person --> Any
Student-->Person
Teacher-->Person
如果一个类实现了多个接口,那么它就会有多个直接父类
1 |
|
graph BT
Run --> Any
Fly --> Any
Bird-->Run
Bird-->Fly
Kotlin 类型检查器强制执行子类型/父类型关系,我们可以将子类型存储到超类型变量中,反过来则不行,这和 java 是一样的逻辑。
另外,kotlin 中还有可空类型,上面我们提到的类型都是非空类型,可空类型只是在可空类型后面加了一个 ?,
1 |
|
从这个角度来看,我们可以认为(仅仅是可以认为)非空类型是对应可空类型的子类,因为非空类型可以赋值给对应的可空类型,反之则不行。
graph BT
Any --> Any?
String? --> Any?
String-->Any
String-->String?
Unit
Kotlin中的Unit即相当于Java中的void关键字,用于表示返回空结果的函数。但这里有一些不一样的地方,当 Unit 用于函数返回值时,是可以省略不写的,但kotlin 还是会认为返回了 Unit。其次,Unit 是一个真实存在的类型,并且是一个单例的。
1 |
|
我们发现,上面这几种写法,效果是一样的。但这里有一个点需要注意一下:
对于unitFun2()
这个函数,跟在函数名后面的Unit
表示返回值类型,而函数体里面的return Unit
中的 Unit 是一个单例对象。虽然看起来什么也没有返回,但它确实返回的一个单例对象。
回到我们上面提到的可空类型上,一个比较边缘的 case:Unit?类型
1 |
|
这个东西我从来没有使用过,是 kotlin 类型一致性的结果
graph BT
Any --> Any?
Unit? --> Any?
Unit-->Any
Unit-->Unit?
那 Unit 这个东西有什么用呢?为啥不沿用 java 中的 void ?
扔物线大佬在他的文章中给出了具体的回答:去特殊化,Unit
去掉了无返回值的函数的特殊性,消除了有返回值和无返回值的函数的本质区别,这样很多事做起来就会更简单了。
例:有返回值的函数在重写时没有返回值
1 |
|
1 |
|
例:函数类型的函数参数
1 |
|
Nothing
这里抛出一个引子:
1 |
|
这个方法的返回值是什么?它真的有返回值么?如果有,是什么类型?
实际上这个方法的返回值类型是Nothing
kotlin中有这么一个方法:TODO
,注意,这里的 TODO 是一个方法,而不是//todo
这一个
1 |
|
我们来看一下 Nothing 定义
1 |
|
这个类无法创建出任何实例,所以所有 Nothing 类型的变量或者函数,都找不到可用的值。那这个东西有啥用?正如它的注释所说, 它可以作为函数「永不返回」的提示,也就是总是抛出异常。
那这个东西有啥用?
第一个作用就是作为函数「永不返回」的提示
第二个作用就是作为泛型对象的临时空白填充,比如
1 |
|
既省事,又省内存。当然也可以用在 Set、Map 中
第三个作用:语法的完整化
Nothing 的「是所有类型的子类型」这个特点,还帮助了 Kotlin 语法的完整化。在 Kotlin 的下层逻辑里,throw 这个关键字是有返回值的,它的返回值类型就是 Nothing。虽然说由于抛异常这件事已经跳出了程序的正常逻辑,所以 throw 返回不返回值、返回值类型是不是 Nothing 对于它本身都不重要,但它让类似 TODO()
函数的写法成为了合法的。
graph BT
String --> Any
Int --> Any
Unit --> Any
Person --> Any
Nothing --> Person
Nothing --> Unit
Nothing --> Int
Nothing --> String
参考
A Whirlwind Tour of the Kotlin Type Hierarchy
Unit 为啥还能当函数参数?面向实用的 Kotlin Unit 详解
这玩意真的有用吗?对,是的!Kotlin 的 Nothing 详解
已学习:
扩展
- 扩展函数
- 扩展属性
- 作用域
函数类型
- 带有接收者的函数类型
- Lambda表达式
- SAM 转换
泛型
- 逆变
- 协变
- 类型投影
- 星投影
- 泛型约束
关键字
- 作用域函数:with、let、run、apply、also
- object:匿名内部类、单例模式、伴生对象
- Unit、Nothing
委托
- 委托类
- 委托属性
- 自定义委托
未学习:
关键字
- inline,noinline,crossinline
协程
- 启动
- 挂起
- Job
- Context
- Channel
- Flow
- select
- 并发、异常
- launch
- Dispatchers
- CoroutineScope