NPE(NullPointerException)是最低级且也最容易犯的错,也是最喜欢遇到的Bug因为好解
本文适合Kotlin入门萌新食用,大佬轻喷哈哈哈!

问号N连帮你空处理(?)

假如服务端返回一个json嵌套了各种数据,映射成POJO大概是这样:

1
2
3
4
5
6
7
8
9
10
11
12
// 此处为简洁省去getter和setter
public class TestParent {
private TestChild child;

public class TestChild {
private List<TestDog> dogs;

public class TestDog {
private String name;
}
}
}

然后,你为了知道他爸的儿子的第一条狗叫啥名,你需要这样:

1
2
3
4
5
6
7
8
9
10
11
12
if (parent != null) { // 有爹
TestParent.TestChild child = parent.getChild();
if (child != null) { // 有儿子
List<TestParent.TestChild.TestDog> dogs = child.getDogs();
if (dogs != null && !dogs.isEmpty()) {
TestParent.TestChild.TestDog dog0 = dogs.get(0);
if (dog0 != null) { // 有狗
System.out.println("name:" + dog0.getName());
}
}
}
}

初学者大部分都会这样写,我的天,这太臃肿了,丑代码,凑行数,于是我决定优化一下(这里不考虑Java 8的API),尽可能减少if嵌套:

1
2
3
4
TestParent.TestChild child = parent != null ? parent.getChild() : null;
List<TestParent.TestChild.TestDog> dogs = child != null ? child.getDogs() : null;
TestParent.TestChild.TestDog dog0 = (dogs != null && !dogs.isEmpty()) ? dogs.get(0) : null;
if (dog0 != null) System.out.println("name:" + dog0.getName());

这种拆分,虽然简洁,但损失了可读性,反应慢的同学一时半会儿还搞不清楚这是在干嘛。
那么用Kotlin的“?”(空处理操作符)怎么做呢?

1
println("name:" + parent?.child?.dogs?.firstOrNull()?.name)

窝草,直接一行就搞定了!当我第一次写出这个代码时,我就被这该死的Kotlin语法糖给甜晕了。
稍微解释一下:
1、println是Kotlin的内置函数,和Java的sout一样。
2、这里用到了Kotlin集合类中的内置函数firstOrNull,顾名思义就知道是去集合的第一个,如果取不到就返回null。
3、? 操作符的含义就是判断左值是否为空,如果不为空就会继续后面的 .xxx 调用,这里就是链式调用,一旦链中有一个环节为空,整个表达式就返回空。
4、结合上面的Java代码看,其实Kotlin的空处理操作符也就是简化了Java判断的三目运算符,因此我在Java当中也可以骚一行:

1
2
System.out.println("name:" + (((parent != null ? parent.getChild() : null) != null ? parent.getChild().getDogs() : null) != null ? (!parent.getChild().getDogs().isEmpty() ? (parent.getChild().getDogs().get(0) != null ? parent.getChild().getDogs().get(0).getName() : null) : null) : null));
// 好了,你疯了吗?我只是想告诉大家有志者事竟成,没有一行代码解决不了的事情哈哈哈哈!

你很确信你的对象不为空(!!)

与空处理相反,!!操作符的含义是非空断言,我们来举个例子看看有无此操作符的区别:
需要空检查:

1
2
3
4
5
6
val files = File("/sdcard/Android").listFiles()
if (files != null) {
for(f in files) {
println(f.name)
}
}

不需要空检查(即人为断言files不为空):

1
2
3
4
val files = File("/sdcard/Android").listFiles()!!
for(f in files) {
println(f.name)
}

谁不空就用谁(?:)

先看一个常用代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void showSoftInput(Context context) {
if (context instanceof Activity) {
View view = ((Activity) context).getCurrentFocus();
if (view == null) {
view = new View(context);
}
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (null != imm) {
view.requestFocus();
imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
}
}
}

这里的view先尝试通过getCurrentFocus获取,如果获取不到再new。类似这样的操作太常见了,甚至还有嵌套几层的。用Elvis操作符(?:)怎么实现呢?

1
2
3
4
5
6
7
8
fun showSoftInput(context: Context) {
if (context is Activity) {
val view = (context.currentFocus ?: View(context)).apply { requestFocus() }
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).apply {
showSoftInput(view, InputMethodManager.SHOW_FORCED)
}
}
}