我们回忆下 Java 里声明一个 View 类型的变量的写法:
☕️
View v;
Kotlin 里声明一个变量的格式是这样的:
🏝️
var v: View
这里有几处不同:
- 有一个
var
关键字 - 类型和变量名位置互换了
- 中间是用冒号分隔的
- 结尾没有分号(对,Kotlin 里面不需要分号)
看上去只是语法格式有些不同,但如果真这么写,IDE 会报错:
🏝️
class Sample {
var v: View
// 👆这样写 IDE 会报如下错误
// Property must be initialized or be abstract
}
这个提示是在说,属性需要在声明的同时初始化,除非你把它声明成抽象的。
-
那什么是属性呢?这里我们可以简单类比 Java 的 field 来理解 Kotlin 的 Property,虽然它们其实有些不一样,Kotlin 的 Property 功能会多些。
-
变量居然还能声明成抽象的?嗯,这是 Kotlin 的功能,不过这里先不理它,后面会讲到。
属性为什么要求初始化呢?因为 Kotlin 的变量是没有默认值的,这点不像 Java,Java 的 field 有默认值:
☕️
String name; // 👈默认值是 null
int count; // 👈默认值是 0
但这些 Kotlin 是没有的。不过其实,Java 也只是 field 有默认值,局部变量也是没有默认值的,如果不给它初始值也会报错:
☕️
void run() {
int count;
count++;
// 👆IDE 报错,Variable ‘count’ might not have been initialized
}
既然这样,那我们就给它一个默认值 null 吧,遗憾的是你会发现仍然报错。
🏝️
class Sample {
var v: View = null
// 👆这样写 IDE 仍然会报错,Null can not be a value of a non-null type View
}
又不行,IDE 告诉我需要赋一个非空的值给它才行,怎么办?Java 的那套不管用了。
其实这都是 Kotlin 的空安全设计相关的内容。很多人尝试上手 Kotlin 之后快速放弃,就是因为搞不明白它的空安全设计,导致代码各种拒绝编译,最终只能放弃。所以咱先别急,我先来给你讲一下 Kotlin 的空安全设计。
Kotlin 的空安全设计
简单来说就是通过 IDE 的提示来避免调用 null 对象,从而避免 NullPointerException。其实在 androidx 里就有支持的,用一个注解就可以标记变量是否可能为空,然后 IDE 会帮助检测和提示,我们来看下面这段 Java 代码:
☕️
@NonNull
View view = null;
// 👆IDE 会提示警告,‘null’ is assigned to a variable that is annotated with @NotNull
而到了 Kotlin 这里,就有了语言级别的默认支持,而且提示的级别从 warning 变成了 error(拒绝编译):
🏝️
var view: View = null
// 👆IDE 会提示错误,Null can not be a value of a non-null type View
在 Kotlin 里面,所有的变量默认都是不允许为空的,如果你给它赋值 null,就会报错,像上面那样。
这种有点强硬的要求,其实是很合理的:既然你声明了一个变量,就是要使用它对吧?那你把它赋值为 null 干嘛?要尽量让它有可用的值啊。Java 在这方面很宽松,我们成了习惯,但 Kotlin 更强的限制其实在你熟悉了之后,是会减少很多运行时的问题的。
不过,还是有些场景,变量的值真的无法保证空与否,比如你要从服务器取一个 JSON 数据,并把它解析成一个 User 对象:
🏝️
class User {
var name: String = null // 👈这样写会报错,但该变量无法保证空与否
}
这个时候,空值就是有意义的。对于这些可以为空值的变量,你可以在类型右边加一个 ?
号,解除它的非空限制:
🏝️
class User {
var name: String? = null
}
加了问号之后,一个 Kotlin 变量就像 Java 变量一样没有非空的限制,自由自在了。
你除了在初始化的时候可以给它赋值为空值,在代码里的任何地方也都可以:
🏝️
var name: String? = “Mike”
…
name = null // 👈原来不是空值,赋值为空值
这种类型之后加 ?
的写法,在 Kotlin 里叫可空类型。
不过,当我们使用了可空类型的变量后,会有新的问题:
由于对空引用的调用会导致空指针异常,所以 Kotlin 在可空变量直接调用的时候 IDE 会报错:
🏝️
var view: View? = null
view.setBackgroundColor(Color.RED)
// 👆这样写会报错,Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type View?
「可能为空」的变量,Kotlin 不允许用。那怎么办?我们尝试用之前检查一下,但似乎 IDE 不接受这种做法:
🏝️
if (view != null) {
view.setBackgroundColor(Color.RED)
// 👆这样写会报错,Smart cast to ‘View’ is impossible, because ‘view’ is