This article is published simultaneously on my wechat official account. You can follow it by scanning the QR code at the bottom of the article or searching Guo Lin on wechat. The article is updated every weekday.
More and more Android developers are using Kotlin.
The language went from being a no-go to being a level 1 language for Android development, to being officially announced by Google as Kotlin First. Kotlin is being accepted and recognized by more and more developers.
Many developers who learn Kotlin have learned Java before, and Kotlin itself is a JVM-based language, so comparisons to Java are inevitable.
Kotlin has a number of features that some developers familiar with Java like and some don’t. But even people who don’t like Kotlin can’t escape the law once they get used to it.
Today I’d like to talk to you about one of Kotlin’s controversial features in the early days: Checked Exceptions.
Because Kotlin does away with Checked Exceptions, this is completely unacceptable to many Java developers, and is probably why many Java supporters reject Kotlin. But Kotlin has been a full-time employee at Google for more than two years now and has created thousands of Android apps. You’ll notice that even without Checked exceptions, Kotlin’s programs don’t have any more problems than Java does, so the need for Checked exceptions in programming languages may not be as great as many people think.
Of course, in this article I can not give a conclusion to prove who is right and who is wrong, but more to share with you my own views and personal experience, in addition to quoting some authoritative opinions of big men.
Also, there is never a right answer to this question, because there is no best programming language in the world (except PHP). Every programming language has its own theory and logic for how it chooses to handle it, so rather than arguing that Java’s Checked Exception mechanism is redundant, it’s better to argue why Kotlin’s lack of one is reasonable.
So let’s start with what a Checked Exception is.
Checked Exception, CE for short. It is a mechanism introduced by programming languages to ensure that programs can better handle and catch exceptions.
Specifically, when a method calls another interface that might throw an exception, either the exception is caught or the exception is thrown and passed to the next layer to catch.
This mechanism will be familiar to anyone familiar with the Java language, as we write programs under its influence almost every day.
Observe the following code:
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(); }}}}Copy the code
This code should be familiar to every Java programmer, this is a section of Java file flow operation code.
There are all sorts of potential exceptions that can happen when we’re doing file flow operations, so they must be caught or thrown, otherwise the program will not compile. That’s Java’s Checked Exception mechanism.
Having Checked Exceptions ensures that your program doesn’t have hidden, latent exceptions that might otherwise explode like a ticking time bomb.
From this point of view, Checked Exceptions are a very necessary mechanism.
There is no Checked Exception in Kotlin, which means that when we use Kotlin to do the above file-flow operations, we can compile without catching or throwing exceptions.
Do developers familiar with Java feel so insecure?
So let’s try to analyze and think about why there are no Checked exceptions in Kotlin.
When I was studying Kotlin, I found that the language used industry best programming practices for many aspects of its design.
For example, the book Effective Java states that if a class is not specifically designed for inheritance, it should be declared final and not inheritable.
In Kotlin, a class cannot be inherited by default unless we actively declare it open.
The list goes on and on.
Therefore, Kotlin’s decision to cancel Checked Exception was definitely not made casually, but supported by a lot of theoretical evidence.
For example, Bruce Eckel, author of Thinking in Java, has publicly stated that Checked Exception in the Java language is a bad decision and Java should remove it. Anders Hejlsberg, the father of C#, agreed with this, so there are no Checked exceptions in C#.
So what’s wrong with the Checked Exception mechanic that most of us Java developers consider necessary?
There are many reasons cited, but I personally think the main reason is just one: trouble.
Checked Exceptions make our programming language more secure, but they can sometimes make us crazy when writing code.
Because of the Checked Exception mechanism, we have to handle code that may have a potential Exception. There are only two ways to handle the exception: use a try catch block to catch the exception, or throw the exception using the throws keyword.
Using the file flow example, we used two try catch blocks for potential exception catching, but it was mostly to keep the compiler happy:
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(); }}}}Copy the code
This code is the most standard and canonical way to write it in Java, but as you’ll see, almost none of us can write any meaningful logic in a Catch, usually just printing an exception message to tell the stream that an exception has occurred. So what happens when a flow exception occurs? No one knows what to do. Theory should always work.
Think about it for a moment. Are all the try catches you put in close streams just to get the compilation to pass? Have you done any meaningful logic in close exception catching?
The Existence of the Checked Exception mechanism forces us to handle uncaught exceptions, even if we explicitly don’t want to.
This mechanism is good in itself, but it also leads to a lot of spoon-feeding code just to satisfy the compiler, resulting in a lot of meaningless try catch statements that make the project code look bloated.
So what if we choose not to catch the exception and instead throw it up? As it turns out, that may not be a particularly good idea either.
Most Java programmers have probably used the reflection API at some point in their lives. The annoying thing about writing reflection code is that the API throws a bunch of exceptions:
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); }Copy the code
Here I just wrote the simplest reflection code, and there are six exceptions waiting for me to handle. I’m not entirely sure what each of these exceptions means, so instead of writing a bunch of try catch code myself, I could just throw all the exceptions up to the next level, so the code looks cleaner.
That’s what you think, that’s what the person at the next level thinks, and what’s worse, he might add another exception to the one you throw and throw it up.
According to the data I reviewed, some projects have gone through this layer upon layer and caught more than 80 exceptions to call an interface. Must be calling this interface in the mind must be abusive. Do you think he would be able to handle every exception type carefully under the circumstances? Absolutely not. Chances are he’ll just catch a top-level Exception, including all of them, and completely disable the Checked Exception mechanism. Or he might add another fire to the current exception throw chain and contribute to the 100 exceptions thrown…
Finally, we can see that Java’s Checked Exception mechanism is indeed well-designed and advanced, but it has high coding specification requirements for programmers. The designer of each layer of methods should be able to clearly identify which exceptions should be caught internally and which should be thrown upwards to keep the chain of exceptions within a reasonable and manageable range throughout the method call stack.
The sad reality is that most programmers can’t do this. Abuse and lazy use of CE is widespread and does not work as intended by Java itself, which is why Kotlin removed Checked Exceptions.
Many Java programmers worry about this. Kotlin has removed the Checked Exception mechanism. Doesn’t that make my program dangerous? Whenever I call a method, I have no idea what exception that method might throw.
First of all, this question has been answered at the beginning. After more than two years of practice, we found that even without Checked exceptions, the programs developed by Kotlin did not have more exceptions than those developed by Java. On the contrary, Kotlin’s program actually reduced the number of exceptions because Kotlin added the ability to handle null-pointer exceptions at compile time (null-pointers consistently rank first in the crash rankings of all languages).
As for why canceling Checked exceptions does not cause more exceptions in programs, I would like to discuss the following points.
First, Kotlin doesn’t prevent you from catching potential exceptions, it just doesn’t force you to.
Most experienced programmers have a good idea of where exceptions are most likely to occur when writing programs. For example, if I am writing network request code, it is highly likely that the request will fail due to network instability, so even without a Checked Exception, most programmers know that they should add a try catch in case the application crashes due to network request failure.
In addition, when you are not sure whether calling a method will result in a potential exception being thrown, you can always be sure by opening the method and observing its throw declaration. Whether you have the source code for this class or not, you can see what exceptions each method throws:
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!" ); }... }Copy the code
Then, when you feel you need to catch the exception, you can catch it again, which means you can still write Kotlin code the same way you used to catch exceptions in Java, but without the mandatory requirement, you can choose whether to catch and throw.
Second, the vast majority of methods don’t actually throw exceptions.
It’s a fact, otherwise you would never love Checked Exception and curse it every day.
Imagine if every line of code you wrote, every method you called, had to try and catch it. Would you want to break your keyboard?
A really good example of this in Java is the thread.sleep () method. Since thread.sleep () throws an InterruptedException, we must use a try catch each time we call it:
public class Main { public void test() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }}}Copy the code
This is why I strongly dislike this method, which is one word: annoying.
In fact, probably the vast majority of Java programmers don’t even know why I’m catching this exception, except that the compiler reminds me that I have to.
The reason we need to catch InterruptedException when calling thread.sleep () is that if we interrupt the sleeping Thread in another Thread (calling thrad.interrupt()) while the current Thread is asleep, The sleep() method ends sleep and throws an InterruptedException. This kind of operation is very rare, but because of Checked Exceptions, we all have to pay for one rare operation: writing a long try catch every time thread.sleep () is called.
With Kotlin, you don’t hate using Thread.sleep() anymore because your code is clean without Checked exceptions:
class Main {
fun test() {
Thread.sleep(1000)
}
}
Copy the code
Third, Java with Checked Exceptions is not that secure.
Some people think that With Checked Exception in Java, you feel comfortable with every method you call because you know what it’s going to throw. Without Checked exceptions, calling any method feels hopeless.
So does that make sense? Obviously this is not true. Otherwise, your Java program should never crash.
In fact, Java divides all exception types into two categories: checked and unchecked. Only Checked exceptions are subject to Checked Exceptions, and not being Checked does not force you to catch or throw an Exception.
For example, like NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException these are not exceptions to be examined, So the Checked Exception mechanism doesn’t require you to catch or throw an Exception in a method you’re calling, even if there’s a null pointer or an out-of-bounds array.
So even with Java’s Checked Exception mechanism, it doesn’t guarantee that every method you call is secure, and I think exceptions like null Pointers and out-of-bounds arrays are far more common than exceptions like InterruptedException, But Java does not protect against this.
I’m also not sure how Java classifies which exceptions are checked and which are not. The Java design team must have had a rationale that didn’t seem to be shared by designers of other languages.
So, you can probably interpret Kotlin as simplifying the exception type further, classifying all exceptions as unchecked exceptions and nothing more.
So, what’s the bottom line?
Unfortunately, there is no conclusion. Just as everything has its diversity, there is no unified conclusion about Checked Exception.
It’s not wrong for Java to have the Checked Exception mechanism, and it’s not wrong for Kotlin to remove the Checked Exception mechanism. I think that’s about all you can conclude after reading this article.
But hopefully, from now on, you won’t have to worry about checking exceptions when programming with Kotlin.
If you want to learn about Kotlin and the latest on Android, check out my new book, Line 1, Version 3. Click here for more details.