Kotlin 朱涛 思维-2 表达式思维 类型 Any Unit Nothing
目录
Kotlin 表达式思维
表达式思维
代码案例
val data = null
val i = data ?: 0 // 如果 data 为 null,则赋值为 0
val j = data ?: 0.also { println("赋值") } // 如果 data 为 null,则赋值同时打印 log
val k = data ?: throw RuntimeException("空") // 如果 data 为 null,则抛异常
val x = when (data) {
is Int -> data
else -> 0
}
val y = try {
"Kotlin".toInt()
} catch (e: NumberFormatException) {
println(e)
0
}
表达式与语句
Kotlin 中,if、when、throw、try-catch 这些语法,都是表达式。
与表达式
对应的另一个概念叫语句
:
- 表达式 :Expression,是一段可以产生值的代码
- 语句 :Statement,是一句不产生值的代码
val a = 1 // statement
val b = 1 + 2 // 1 + 2 是一个表达式,但是对 b 的赋值行为是 statement
fun min(a: Int, b: Int) = if (a > b) b else a // if else 整体是一个表达式
fun cal(): Int = throw NotImplementedError() // throw xxx 是一个表达式
- 语句与表达式,可能会出现在同一行代码中,比如 val b = 1 + 2
- if 等 Kotlin 语法,既可作为语句,也可作为表达式
- 表达式中可以包含
子表达式
- throw 语句也可以作为表达式
Kotlin 的类型系统
要真正理解 Kotlin 表达式背后的原理,需要了解 Kotlin 的类型系统
。
Any 与 Any? 与 Object
Any
是所有 非空类型 的根类型Any?
是所有 Kotlin 类型的根类型,大致对应 Java 中的Object
类型Any
与Any?
在定义时,并没有明确的继承关系,但它们又存在父子类型的关系- 可以将
Any
看作是Any?
的子类,将String
看作是String?
的子类
val a: Any = ""
val b: Any? = a // 通过,Any 类型可以赋值给 Any? 类型
val c: Any = b // 报错,Any? 类型不可以赋值给 Any 类型
注解 Nullable 与 NotNull
Java 中的 Object
类型与 Kotlin 的 Any?
类型大致对应,但不完全等价。
@Nullable public Object test1() { return null; } // 返回 Object 类型,带可空注解
public Object test2() { return null; } // 返回 Object 类型,无任何注解
@NotNull public Object test3() { return 1; } // 返回 Object 类型,带非空注解
将上面的 Java 代码转换成 Kotlin 以后,会变成这样:
fun test1(): Any? { return null } // 有可空注解时,Object 类型被换行成了 Any?
fun test2(): Any? { return null } // 无任何注解时,Object 类型被换行成了 Any?
fun test3(): Any { return 1 } // 油非空注解时,Object 类型被换行成了 Any
由此可见,在有了注解
修饰以后,Kotlin 就能够识别出到底是 Any
,还是 Any?
。
Unit 与 Unit? 与 Void 与 void
Unit
- 在 Java 当中,Void 是一个普通的 Java 类(class),void 是一个用于 修饰方法的关键字
- Kotlin 中的 Unit 兼具 Java 中 Void 和 void 两种功能
- Unit 也是一个普通的类,只不过 Kotlin 编译器会特殊对待它
- Unit 也是用来声明方法的返回值的,当 Unit 作为返回值类型时,表明方法没有返回值,此时可以省略 return
fun fun1(): Unit { }
fun fun2(): Unit { return }
fun fun3(): Unit { return Unit }
fun fun4(): Unit = Unit
Kotlin 的 Unit 与 Java 的 Void 或者 void 并不存在等价的关系,但它们之间确实存在一些概念上的相似性。
Unit?
Kotlin 编译器只会把 Unit
类型当作无需返回值
的类型,而其对应的可空类型 Unit?
则不行。所以,Unit?
这个类型其实没有什么实际的应用场景。
当 Unit?
作为返回值的时候,函数必须要 return 一个值,且返回值的类型只能是 null
、Unit 单例
、Nothing
这三种情况。
fun fun1(): Unit? { } // 报错,缺少 return
fun fun2(): Unit? { return } // 报错,缺少 return
fun fun3(): Unit? { return Unit } // 通过
fun fun4(): Unit? = Unit // 通过
fun fun5(): Unit? = null // 通过
fun fun6(): Unit? = throw Exception() // 通过
Unit 与泛型
当 T 类型作为返回值类型的时候,如果 T 的实际类型为 Unit,可以省略 return。
interface Task<T> {
fun excute(any: Any): T // 返回值类型
}
class PrintTask: Task<Unit> {
override fun excute(any: Any) {
println(any)
// 可以省略 return
}
}
Unit 与函数类型
借助 Unit,才有了 Kotlin 中的函数类型。
val f: () -> Unit = {}
Nothing 和 Nothing?
Nothing
Any?
是所有的 Kotlin 类型的父类型Nothing
则是所有 Kotlin 类型的 子类型,在函数式编程中,也叫做底类型(Bottom Type)Nothing
可以看作是Nothing?
子类型
Nothing 与 throw
因为 throw 表达式的返回值是 Nothing 类型,而 Nothing 是所有类型的子类型,所以 throw 表达式可以赋值给任意类型。
fun f1(): Int = throw Throwable()
fun f2(): Any = throw Error()
fun f3(): Unit = throw Exception()
fun f4(): Nothing = throw Exception()
fun f5(): Nothing? = throw Exception()
无法构造 Nothing 实例
Nothing 构造函数是私有
的,因此无法直接构造它的实例:
public class Nothing private constructor() // Nothing 的构造函数是私有的
这就导致:
- 当 Nothing 作为
函数参数
的时候,这个函数将永远 无法被正常调用- 除非我们 主动抛出异常,但这没有意义
- 这在泛型
星投影
的时候是有一定应用的 -- 我还不知道啥用
- 当 Nothing 作为
函数返回值
的时候,这个函数永远 无法正常返回结果- 除非我们 主动抛出异常,但这同样会截断程序的后续流程
- 意味着它后面的语句不再有机会被执行,Kotlin 编译器可据此进行流程分析
fun f1(n: Nothing) = println("哈哈哈") // 参数类型是 Nothing,由于无法构造 Nothing,所以函数体不会执行
fun f2(): Nothing = throw Exception() // 返回值类型是 Nothing,只能主动抛出异常,导致后面的代码无法执行
fun main() {
f1(throw Exception()) // 主动抛出异常,不报错,但会提示:Unreachable code
f1(null) // 报错:Null can not be a value of a non-null type Nothing
f2() // 这个方法里面会抛出异常,所以后面的代码是无法执行的
println("代码不可触及") // 这里会提示:Unreachable code
}
Nothing?
Nothing?
没有 Nothing
的一系列特性与限制,可以看作是 Nothing
的父类型,也可以看作是一个非常普通的类型。
- 使用
Nothing?
作为返回值类型的函数是一个普通的函数,可以返回null
或者是抛出异常 - 使用
Nothing?
作为参数类型的函数是一个普通的函数,可以传入null
来调用函数,或者是抛出异常
fun f1(n: Nothing?) = println("哈哈哈") // 参数类型是 Nothing?,可以传入 null
fun f2(): Nothing? = null // 返回值类型是 Nothing?,可以返回 null
fun f3(): Nothing? = throw Exception() // 返回值类型是 Nothing?,可以抛出异常
fun main() {
f1(null) // 传入 null
val n: Nothing? = f2()
println("返回值为 $n") // 正常打印【返回值为 null】
f3() // 这个方法里面会抛出异常,所以后面的代码是无法执行的
println("代码不可触及") // 不会提示代码有问题
f1(throw Exception()) // 抛出异常,编译不报错
}
表达式的本质
Kotlin 的类型系统让大部分的语句都变成了表达式,同时也让无返回值的函数有了类型。
- 表达式是一段可以产生值的代码,而语句则是一句不产生值的代码
- Kotlin 中的大部分代码都是可以作为表达式的,也即都是有
返回值
的 - Kotlin 类型系统中的 Unit 和 Nothing,让很多原本无法产生返回值的语句,可以变成表达式
fun main() {
println("bqt") // statement
val a: Unit = println("bqt") // 上面的语句变成了表达式,它所产生的值就是 Unit 这个单例
throw Exception() // statement
val b: Nothing = throw Exception() // 表达式,它所产生的值是 Nothing 类型
fun test() = throw Exception() // 表达式,它所产生的值是 Nothing 类型
}
2016-05-12
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/5487889.html