关于android:浅谈Kotlin的Checked-Exception机制

64次阅读

共计 7086 个字符,预计需要花费 18 分钟才能阅读完成。

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜寻 郭霖 即可关注,每个工作日都有文章更新。

当初应用 Kotlin 的 Android 开发者曾经越来越多了。

这门语言从一开始的无人问津,到起初成为 Android 开发的一级语言,再到起初 Google 官宣的 Kotlin First。Kotlin 正在被越来越多的开发者承受和认可。

许多学习 Kotlin 的开发者之前都是学习过 Java 的,并且自身 Kotlin 就是一款基于 JVM 语言,因而不可避免地须要常常和 Java 进行比拟。

Kotlin 的诸多个性,在相熟 Java 的开发者看来,有些人很喜爱,有些人不喜爱。但即便是不喜爱的那些人,一旦用熟了 Kotlin 进行程序开发之后,也难逃真香定律。

明天我想跟大家聊一聊的话题,是 Kotlin 在晚期的时候争议比拟大的一个个性:Checked Exception 机制。

因为 Kotlin 勾销了 Checked Exception,这在很多 Java 开发者看来是齐全不可承受的,可能也是许多 Java 支持者回绝应用 Kotlin 的起因。但目前 Kotlin 曾经被 Google 转正两年多了,开发了成千上万的 Android 利用。你会发现,即便没有 Checked Exception,Kotlin 编写出的程序也并没有呈现比 Java 更多的问题,因而编程语言中对于 Checked Exception 的必要性可能并没有许多人设想中的那么高。

当然,本篇文章中我并不能给出一个论断来证实谁对谁错,更多的是跟大家谈一谈我本人的观点和集体心得,另外援用一些大佬的权威观点。

另外,这个问题永远是没有正确答案的,因为世界上没有最好的编程语言(PHP 除外)。每个编程语言抉择不同的解决形式都有着本人的一套实践和逻辑,所以与其去争执 Java 中的 Checked Exception 机制是不是多余的,不如去论证 Kotlin 中没有 Checked Exception 机制为什么是正当的。

那么,咱们首先从什么是 Checked Exception 开始说起。

什么是 Checked Exception?

Checked Exception,简称 CE。它是编程语言为了保障程序可能更好的解决和捕捉异样而引入的一种机制。

具体而言,就是当一个办法调用了另外一个可能会抛出异样的接口时,要么将这个异样进行捕捉,要么将这个异样抛出,交给上一层进行捕捉。

相熟 Java 语言的敌人对这一机制肯定不会生疏,因为咱们简直每天都在这个机制的影响下编写程序。

察看如下代码:

public void readFromFile(File file) {
    FileInputStream in = null;
    BufferedReader reader = null;
    StringBuilder content = new StringBuilder();
    try {in = new FileInputStream(file);
        reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while ((line = reader.readLine()) != null) {content.append(line);
        }
    } catch (IOException e) {e.printStackTrace();
    } finally {if (reader != null) {
            try {reader.close();
            } catch (IOException e) {e.printStackTrace();
            }
        }
    }
}

这段代码每位 Java 程序员应该都十分相熟,这是一段 Java 文件流操作的代码。

咱们在进行文件流操作时有各种各样潜在的异样可能会产生,因而这些异样必须被捕捉或者抛出,否则程序将无奈编译通过,这就是 Java 的 Checked Exception 机制。

有了 Checked Exception,就能够保障咱们的程序不会存在一些暗藏很深的潜在异样,不然的话,这些异样会像定时炸弹一样,随时可能会引爆咱们的程序。

由此看来,Checked Exception 是一种十分有必要的机制。

为什么 Kotlin 中没有 CE?

Kotlin 中是没有 Checked Exception 机制的,这意味着咱们应用 Kotlin 进行文件流操作时,即便不捕捉或者抛出异样,也能够失常编译通过。

相熟 Java 的开发者们是不是感觉这样重大没有安全感?

那么咱们就来尝试剖析和思考一下,为什么 Kotlin 中没有 Checked Exception。

我在学习 Kotlin 时,发现这门语言在很多设计方面都参考了一些业内的最佳编程实际。

举个例子,《Effective Java》这本书中有提到过,如果一个类并非是专门为继承而设计的,那么咱们就应该将它申明成 final,使其不可被继承。

而在 Kotlin 当中,一个类默认就是不可被继承的,除非咱们被动将它申明成 open。

相似的例子还有很多很多。

因而,Kotlin 勾销 Checked Exception 也必定不是随随便便拍脑瓜决定的,而是有很多的理论依据为其反对。

比如说,《Thinking in Java》的作者 Bruce Eckel 就已经公开示意,Java 语言中的 Checked Exception 是一个谬误的决定,Java 应该移除它。C# 之父 Anders Hejlsberg 也认同这个观点,因而 C# 中是没有 Checked Exception 的。

那么咱们大多数 Java 开发者都认为十分有必要的 Checked Exception 机制到底存在什么问题呢?

这些大佬们例举了很多方面的起因,然而我集体认为最次要的起因其实就是一个:麻烦。

Checked Exception 机制尽管晋升了编程语言的安全性,然而有时却让咱们在书写代码时相当抓狂。

因为 Checked Exception 机制的存在,对于一些可能产生潜在异样的代码,咱们必须要对其进行解决才行。解决形式只有两种:要么应用 try catch 代码块将异样捕捉住,要么应用 throws 关键字将异样抛出。

以方才的文件流操作举例,咱们应用了两次 try catch 代码块来进行潜在的异样捕捉,但其实更多只是为了能让编译器称心:

public void readFromFile(File file) {
    BufferedReader reader = null;
    try {...} catch (IOException e) {e.printStackTrace();
    } finally {if (reader != null) {
            try {reader.close();
            } catch (IOException e) {e.printStackTrace();
            }
        }
    }
}

这段代码在 Java 当中是最规范和标准的写法,然而你会发现,咱们简直没有人能在 catch 中写出什么有意义的逻辑解决,通常都只是打印一下异样信息,告知流产生异样了。那么流产生异样应该怎么办呢?没人晓得应该怎么办,实践上流应该总是能失常工作的。

思考一下,是不是你在 close 文件流时所加的 try catch 都只是为了可能让编译通过而已?你有在 close 的异样捕捉中进行过什么有意义的逻辑解决吗?

而 Checked Exception 机制的存在强制要求咱们对这些未捕捉的异样进行解决,即便咱们明确不想对它进行解决都不能够。

这种机制的设计思路自身是好的,然而却也间接造就了很多填鸭式的代码,只是为了满足编译器去编程,导致编写了很多无意义的 try catch 语句,让我的项目代码看来得变得更加臃肿。

那么如果咱们抉择不对异样进行捕捉,而是将异样向上抛出呢?事实证明,这可能也并不是什么特地好的主见。

绝大多数 Java 程序员应该都应用过反射的 API,编写反射代码时有一点特地厌恶,就是它的 API 会抛出一大堆的异样:

Object reflect(Object object, String className, String methodName, Object[] parameters, Class<?>[] parameterTypes)
        throws SecurityException, IllegalArgumentException, 
        IllegalAccessException, InvocationTargetException, 
        NoSuchMethodException, ClassNotFoundException {Class<?> objectClass = Class.forName(className);
    Method method = objectClass.getMethod(methodName, parameterTypes);
    return method.invoke(object, parameters);
}

这里我只是编写了一段最简略的反射代码,居然有 6 个异样要等着我去解决。其中每个异样代表什么意思我也没能齐全搞明确,与其我本人去写一大堆的 try catch 代码,还不如间接将所有异样都抛出到上一层得了,这样代码看起来还能清新一点。

你是这么想的,上一层的人也是这么想的,更过分的是,他可能还会在你抛出异样的根底之上,再减少一点其余的异样持续往上抛出。

依据我查阅到的材料,有些我的项目通过这样的层层累加之后,调用一个接口甚至须要捕捉 80 多个异样。想必调用这个接口的人心里肯定在骂娘吧。你感觉在这种状况下,他还能急躁地对每一种异样类型都仔细进行解决吗?相对不可能,大概率可能他只会 catch 一个顶层的 Exception,把所有异样都囊括进去,从而彻底地让 Checked Exception 机制失去意义。又或者,他可能会在以后异样抛出链上再加一把火,为抛出 100 个异样做出奉献。。。

最终咱们能够看出,Java 的 Checked Exception 机制,自身的设计初衷的确是好的,而且是先进的,然而却对程序员有着较高的编码标准要求。每一层办法的设计者都应该能分明地分别哪些异样是应该本人外部捕捉的,哪些异样是应该向上抛出的,从而让整个办法调用栈的异样链都在一个正当和可控的范畴内。

然而比拟遗憾的事实是,绝大多数的程序员其实都是做不到这一点的,滥用和惰性应用 CE 机制的状况宽泛存在,齐全达不到 Java 自身设计这个机制所预期的成果,这也是 Kotlin 勾销 Checked Exception 的起因。

没有 CE 不会呈现问题吗?

许多 Java 程序员会比拟放心这一点,Kotlin 勾销了 Checked Exception 机制,这样不会导致我的程序变得很危险吗?每当我调用一个办法时,都齐全不晓得这个办法可能会抛出什么异样。

首先这个问题在结尾曾经给出了答案,通过两年多的实际发现,即便没有 Checked Exception,Kotlin 开发出的程序也并没有比 Java 开发的程序呈现更多的异样。恰恰相反,Kotlin 程序反倒是缩小了很多异样,因为 Kotlin 减少了编译期解决空指针异样的性能(空指针在各类语言的解体率排行榜中都始终排在第一位)。

那么至于为什么勾销 Checked Exception 并不会成为导致程序呈现更多异样的起因,我想分成以下几个点探讨。

第一,Kotlin 并没有阻止你去捕捉潜在的异样,只是不强制要求你去捕捉而已。

经验丰富的程序员在编写程序时,哪些地方最有可能产生异样其实大多是成竹在胸的。比方我正在编写网络申请代码,因为网络存在不稳定性,申请失败是极有可能产生的事件,所以即便没有 Checked Exception,大多数程序员也都晓得应该在这里加上一个 try catch,避免因为网络申请失败导致程序解体。

另外,当你不确定调用一个办法会不会有潜在的异样抛出时,你永远能够通过关上这个办法,察看它的抛出申明来进行确定。不论你有没有这个类的源码都能够看到它的每个办法抛出了哪些异样:

public class FileInputStream extends InputStream {public FileInputStream(File file) throws FileNotFoundException {throw new RuntimeException("Stub!");
    }

    public int read(byte[] b, int off, int len) throws IOException {throw new RuntimeException("Stub!");
    }

    public void close() throws IOException {throw new RuntimeException("Stub!");
    }
    ...
}

而后当你感觉须要对这个异样进行捕捉时,再对它进行捕捉即可,相当于你依然能够依照之前在 Java 中捕捉异样的形式去编写 Kotlin 代码,只是没有了强制的要求,你能够自由选择要不要进行捕捉和抛出。

第二,绝大多数的办法其实都是没有抛出异样的。

这是一个事实,不然你相对不会爱上 Checked Exception 机制,而是会天天谩骂它。

试想一下,如果你编写的每一行代码,调用的每一个办法,都必须要对它 try catch 捕捉一下才行,你是不是想摔键盘的心都有了?

我说的这种状况在 Java 中真的有一个十分典型的例子,就是 Thread.sleep() 办法。因为 Thread.sleep() 办法会抛出一个 InterruptedException,所以每次咱们调用这个办法时,都必须要用 try catch 捕捉一下:

public class Main {public void test() {
        // do something before
        try {Thread.sleep(1000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        // do something after
    }
    
}

这也是我极其不喜爱这个办法的起因,用起来就是一个字:烦。

事实上,可能绝大多数 Java 程序员甚至都不晓得为什么要捕捉这个异样,只晓得编译器揭示我必须捕捉。

之所以咱们在调用 Thread.sleep() 办法时须要捕捉 InterruptedException,是因为如果在以后线程睡眠的过程中,咱们在另外一个线程对中这个睡眠中的线程进行中断(调用 thrad.interrupt() 办法),那么 sleep() 办法会完结休眠,并抛出一个 InterruptedException。这种操作是十分少见的,然而因为 Checked Exception 的存在,咱们每个人都须要为这一个少见的操作买单:即每次调用 Thread.sleep() 办法时,都要写一段长长的 try catch 代码。

而到了 Kotlin 当中,你会不再厌恶应用 Thread.sleep() 办法,因为没有了 Checked Exception,代码也变得清新了:

class Main {fun test() {
        // do something before
        Thread.sleep(1000)
        // do something after
    }

}

第三,领有 Checked Exception 的 Java 也并不是那么平安。

有些人认为,Java 中领有 Checked Exception 机制,调用的每个办法你都会感到释怀,因为晓得它会抛出什么异样。而没有 Checked Exception 的话,调用任何办法心里都感觉没底。

那么这种说法有情理吗?显然这不是真的。不然,你的 Java 程序应该永远都不会解体才对。

事实上,Java 将所有的异样类型分成了两类:受查看异样和不受查看异样。只有受查看异样才会受到 Checked Exception 机制的束缚,不受查看异样是不会强制要求你对异样进行捕捉或抛出的。

比如说,像 NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 这些都是不受查看的异样,所以你调用的办法中即便存在空指针、数组越界等异样危险,Checked Exception 机制也并不会要求你进行捕捉或抛出。

由此可见,即便 Java 领有 Checked Exception 机制,也并不能向你保障你调用的每个办法都是平安的,而且我认为空指针和数组越界等异样要远比 InterruptedException 之类的异样更加常见,但 Java 并没有对此进行爱护。

至于 Java 是如何划分哪些异样属于受查看异样,哪些属于不受查看异样,这个我也不太分明。Java 的设计团队肯定有本人的一套理论依据,只不过这套理论依据看上去并没有被其余语言的设计者所认可。

因而,你大略能够了解成,Kotlin 就是把异样类型进一步进行了简化,将所有异样都归为了不受查看异样,仅此而已。

论断

所以,最终的论断是什么呢?

很遗憾,没有论断。正如任何事物都有其多样性一样,对于 Checked Exception 这个问题下面,也没有一个对立的定论。

Java 领有 Checked Exception 机制并不是谬误的,Kotlin 中勾销 Checked Exception 机制也不是谬误的。我想这大略就是你浏览完本文之后可能得出的论断吧。

然而,心愿你自此往后,在应用 Kotlin 编程程序时,不要再为有没有 Checked Exception 的问题所纠结了。

如果想要学习 Kotlin 和最新的 Android 常识,能够参考我的新书 《第一行代码 第 3 版》,点击此处查看详情。

关注我的技术公众号,每个工作日都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

<img src=”https://img-blog.csdnimg.cn/20181224140138240.jpg” width=”200px”>

正文完
 0