Kotlin操作符快速获取爸爸的儿子的狗的名字

124 阅读3分钟

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

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

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

// 此处为简洁省去getter和setter
public class TestParent {
    private TestChild child;

    public class TestChild {
        private List<TestDog> dogs;

        public class TestDog {
            private String name;
        }
    }
}

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

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嵌套:

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的“?”(空处理操作符)怎么做呢?

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

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

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));
// 好了,你疯了吗?我只是想告诉大家有志者事竟成,没有一行代码解决不了的事情哈哈哈哈!

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

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

val files = File("/sdcard/Android").listFiles()
if (files != null) {
	for(f in files) {
		println(f.name)
	}
}

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

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

谁不空就用谁(?:)

先看一个常用代码块:

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操作符(?:)怎么实现呢?

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)
        }
    }
}