本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜寻 郭霖 即可关注,每个工作日都有文章更新。
当初应用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">