现在使用Kotlin的Android开发者越来越多。从一开始没人关心这门语言,到后来成为Android开发的第一级语言,再到后来被谷歌官方宣布为KotlinFirst。Kotlin正在被越来越多的开发者所接受和认可。很多学习Kotlin的开发者之前都学过Java,而Kotlin本身就是一门基于JVM的语言,所以不免经常拿它和Java进行比较。Kotlin的很多特性,在熟悉Java的开发者眼中,有人非常喜欢,也有人不喜欢。但即使是不喜欢的人,一旦使用Kotlin进行程序开发,也难逃真香定律。今天要和大家聊的话题是Kotlin早期颇受争议的一个特性:CheckedException机制。由于Kotlin取消了CheckedException,这对于很多Java开发者来说是完全不能接受的,这可能也是很多Java支持者拒绝使用Kotlin的原因。但目前,Kotlin已经通过谷歌认证两年多了,已经开发出上千款安卓应用。你会发现即使没有CheckedException,用Kotlin写的程序也不会比Java多出问题,所以CheckedException在编程语言中的必要性可能没有很多人想的那么高。当然,在本文中,我无法给出结论来证明谁对谁错。更多的是和大家谈谈自己的看法和亲身经历,引用一些大佬们的权威观点。另外,这个问题永远没有正确答案,因为世界上没有最好的编程语言(PHP除外)。每种编程语言都有自己的一套理论和逻辑来选择不同的处理方式,与其争论Java中的CheckedException机制是否多余,不如论证为什么Kotlin中没有CheckedException机制。那么,让我们从什么是CheckedException开始。/什么是检查异常?/CheckedException,简称CE。它是编程语言为了保证程序能够更好地处理和捕获异常而引入的一种机制。具体来说,当一个方法调用另一个可能抛出异常的接口时,要么捕获异常,要么抛出异常交给上层捕获。熟悉Java语言的朋友一定对这种机制不陌生,因为我们几乎每天都在这种机制的影响下写程序。观察以下代码:publicvoidreadFromFile(Filefile){FileInputStreamin=null;BufferedReaderreader=null;StringBuildercontent=newStringBuilder();try{in=newFileInputStream(file);reader=newBufferedReader(newInputStreamReader(in));Stringline="";while((line=reader.readLine())!=null){content.append(line);}}catch(IOExceptione){e.printStackTrace();}finally{if(reader!=null){try{reader.close();}catch(IOExceptione){e.printStackTrace();}}}}这段代码每个Java程序员应该都非常熟悉。这是Java文件流操作的一段代码。当我们进行文件流操作时,可能会出现各种潜在的异常,所以这些异常必须被捕获或抛出,否则程序将无法编译。这就是Java的CheckedException机制。有了CheckedException,我们就可以保证我们的程序不会出现一些隐藏的潜在异常,否则,这些异常就会像定时炸弹一样,随时可能引爆我们的程序。从这个角度来看,CheckedException是一个非常必要的机制。/为什么Kotlin中没有CE?/Kotlin中没有CheckedException机制,这意味着当我们使用Kotlin进行上述文件流操作时,即使没有捕获或抛出异常,编译仍然可以正常通过。熟悉Java的开发者会不会觉得这样严重不安全?那我们就试着分析思考一下为什么Kotlin中没有CheckedException。当我在学习Kotlin的时候,我发现这门语言在很多设计方面都参考了业界一些最佳的编程实践。比如《Effective Java》一书中提到,如果一个类不是专门为继承而设计的,那么我们就应该将它声明为final,这样它就不能被继承。在Kotlin中,一个类默认是不能被继承的,除非我们主动声明它是开放的。类似的例子还有很多很多。因此,Kotlin取消CheckedException绝对不是随便的决定,而是有很多理论依据支持的。比如《Thinking in Java》的作者BruceEckel就曾公开表示,Java语言中的CheckedException是一个错误的决定,Java应该将其移除。C#之父AndersHejlsberg也同意这个观点,所以C#中没有CheckedException。那么我们大多数Java开发者认为非常有必要的CheckedException机制有什么问题呢?这些大佬列举了很多原因,但我个人认为最主要的原因其实就是一个:麻烦。CheckedException机制虽然提高了编程语言的安全性,但是有时候写代码的时候也会让我们相当抓狂。由于CheckedException机制的存在,我们必须对一些潜在的异常代码进行处理。处理方法只有两种:要么使用trycatch代码块捕获异常,要么使用throws关键字抛出异常。以刚才的文件流操作为例,我们使用了两个trycatch代码块来捕捉潜在的异常,但实际上更多的只是为了满足编译器:publicvoidreadFromFile(Filefile){BufferedReaderreader=null;try{..}catch(IOExceptione){e.printStackTrace();}最后{if(reader!=null){try{reader.close();}catch(IOExceptione){e.printStackTrace();}}}}这段代码是Java中最标准、规范的写法。但是,你会发现我们几乎没有人能在catch中写出任何有意义的逻辑处理。通常,我们只是打印异常信息来通知流发生了异常。那么推流失败怎么办?没有人知道该怎么做,理论上流应该始终正常工作。想一想,你在关闭文件流时加的trycatch真的只是为了让编译通过吗?在close的异常捕获中有没有做过有意义的逻辑处理?CheckedException机制的存在迫使我们必须处理这些未捕获的异常,即使我们明确不想处理它们。这种机制的设计思路本身是好的,但是也间接的造就了很多填鸭式的代码,就是为了满足编译器去编程,导致写了很多无意义的trycatch语句,让项目代码看起来比较复杂。臃肿。那么如果我们选择不捕获异常,而是向上抛出异常呢?事实证明,这可能不是一个特别好的主意。绝大多数Java程序员应该都用过反射API。写反射代码时特??别烦人的一件事是它的API会抛出很多异常:NoSuchMethodException,ClassNotFoundException{Class>objectClass=Class.forName(className);Methodmethod=objectClass.getMethod(methodName,parametertypes.parameterTypes)odrect;);}这里我只是写了最简单的反射代码,还有6个异常等着我去处理。我还没有完全理解每个异常的含义。与其自己写一大堆trycatch的代码,还不如直接把异常都抛给上层,这样代码看起来清爽。一点。你是这么想的,上层的人也是这么想的。更有什者,他可能会在你抛出的异常的基础上,再加一点异常继续抛。根据我查到的资料,有些项目积累到这样的层数之后,调用一个接口甚至需要捕获80多个异常。想必调用这个接口的人心里一定在骂。你认为在这种情况下,他能耐心细致地处理每一种异常类型吗?绝对不可能,大概率他只会捕获一个顶级的Exception,把所有的异常都包含进来,从而彻底让CheckedException机制失去意义。或者,他可能会为当前的异常抛出链添加火力,导致抛出100个异常。..最后我们可以看到,Java的CheckedException机制本身是用心设计的,是先进的,但是对程序员的编码标准要求很高。每一层方法的设计者都应该能够清楚地识别出哪些异常应该在内部捕获,哪些异常应该向上抛出,从而使整个方法调用栈的异常链处于一个合理可控的范围内。然而,令人遗憾的现实是,绝大多数程序员其实做不到这一点。CE机制的滥用和偷懒现象很普遍,并没有达到Java自己设计这种机制的预期效果。Kotlin之所以取消CheckedException。/没有CE就不会有问题吗?/很多Java程序员会比较担心这个。Kotlin取消了CheckedException机制。这不会使我的程序变得非常危险吗?每当我调用一个方法时,完全不确定这个方法可能会抛出什么异常。首先,这个问题一开始就已经给出了答案。经过两年多的实践,发现即使没有CheckedException,Kotlin开发的程序也不会比Java开发的程序出现更多的异常。相反,Kotlin程序减少了很多异常,因为Kotlin在编译时加入了处理空指针异常的功能(空指针在各种语言的崩溃率排行榜上一直排在第一位)。那么至于为什么取消CheckedException不会导致程序出现更多的异常,我想分以下几点来讨论。首先,Kotlin不会阻止您捕获潜在的异常,它只是不会强迫您这样做。有经验的程序员在写程序的时候,大多都知道哪里最容易出现异常。比如我在写一个网络请求代码。由于网络的不稳定,很可能会出现请求失败的情况,所以即使没有CheckedException,大部分程序员都知道这里应该加一个trycatch来防止网络失败。请求失败导致程序崩溃。此外,当您不确定调用某个方法是否会潜在地抛出异常时,您始终可以通过打开该方法并观察其throw语句来确定。不管你有没有这个类的源代码,你都可以看到它的每个方法抛出了哪些异常:,intlen)throwsIOException{thrownewRuntimeException("Stub!");}publicvoidclose()throwsIOException{thrownewRuntimeException("Stub!");}...}然后当你认为你需要捕获这个异常时,再捕获它,它相当于你仍然可以按照之前Java中捕获异常的方式编写Kotlin代码,但是没有强制要求,你可以自由选择是否捕获和抛出。其次,绝大多数方法实际上并不抛出异常。这是事实,否则你永远不会爱上CheckedException机制,反而会天天骂它。试想一下,如果你写的每一行代码,调用的每一个方法都要被trycatch捕获,你是不是想把键盘扔了?我说的这种情况在Java中确实有一个很典型的例子,就是Thread.sleep()方法。由于Thread.sleep()方法会抛出InterruptedException,所以我们每次调用这个方法,都要用trycatch来捕获:publicclassMain{publicvoidtest(){//dosomethingbeforetry{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}//dosomethingafter}}这也是我不太喜欢这种方法的原因。就一个字:烦人。其实可能绝大多数Java程序员都不知道为什么要捕获这个异常,只是编译器提醒我必须捕获。之所以在调用Thread.sleep()方法时需要捕获InterruptedException,是因为如果我们在当前线程休眠时中断另一个线程对中的休眠线程(调用thrad.interrupt()方法),那么sleep()方法将结束睡眠并抛出InterruptedException。这种操作是非常少见的,但是由于CheckedException的存在,我们每个人都需要为这种少见的操作付出代价:即每次调用Thread.sleep()方法时,都要进行一段很长的trycatch代码写的。而在Kotlin中,你将不再讨厌使用Thread.sleep()方法,因为没有了CheckedException,代码变得清爽:classMain{funtest(){//dosomethingbeforeThread.sleep(1000)//dosomethingafter}}第三,带有CheckedException的Java并不是那么安全。有人认为Java有CheckedException机制,你调用每一个方法你都会安心,因为你知道它会抛出什么异常。没有CheckedException,调用任何方法都感觉不确定。那么这种说法有什么道理吗?显然这不是真的。否则,您的Java程序永远不会崩溃。实际上,Java将所有的异常类型分为两类:checkedexceptions和uncheckedexceptions。只有checkedexceptions才会受制于CheckedException机制,uncheckedexceptions不会强制你捕获或抛出异常。比如NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException都是uncheckedexception,所以即使你调用的方法中存在空指针、数组越界等异常风险,CheckedException机制也不会要求你catch或者throw。由此可见,即使Java有CheckedException机制,也不能保证你调用的每一个方法都是安全的,我认为空指针、数组越界等异常比InterruptedException等异常更常见,但Java不对此提供保护。至于Java是怎么划分哪些异常是checked异常,哪些是unchecked异常,这个我不是很了解。Java设计团队肯定有自己的一套理论基础,但是这套理论基础似乎并没有被其他语言的设计者认可。因此,你大概可以理解为Kotlin进一步简化了异常类型,将所有异常归类为未检查异常,仅此而已。/结论/那么,最后的结论是什么?遗憾的是,没有结论。正如万事万物都有其多样性一样,关于CheckedException的问题也没有统一的定论。Java有CheckedException机制没有错,Kotlin取消CheckedException机制也没有错。我想这大概是你看完这篇文章可以得出的结论。不过,希望以后大家在使用Kotlin编程的时候,不要再纠结于是否有CheckedException这个问题。本文转载自微信?「果林」,可关注下方二维码。转载本文请联系郭琳公众号。
