- Исключения
- Try — это выражение
- Проверяемые исключения
- Тип Nothing
- Совместимость с Java
- Exceptions
- Try is an expression
- Checked exceptions
- The Nothing type
- Java interoperability
- Exception Handling in Kotlin
- 1. Overview
- 2. Exceptions
- 3. Kotlin Exceptions
- 4. Try-Catch Block
- 4.1. Try-Catch Block as an Expression
- 4.2. Multiple Catch Blocks
- 4.3. Nested Try-Catch Block
- 5. Finally Block
- 6. Throw Keyword
- 7. Throws Annotation
- 8. Conclusion
Исключения
Все исключения в Kotlin являются наследниками класса Throwable . У каждого исключения есть сообщение, трассировка стека и (опционально) причина, по которой это исключение вероятно было вызвано.
Для того чтобы возбудить исключение явным образом, используйте оператор throw .
Чтобы перехватить исключение, используйте выражение try . catch .
try < // some code >catch (e: SomeException) < // handler >finally < // optional finally block >
В коде может быть любое количество блоков catch (такие блоки могут и вовсе отсутствовать). Блоки finally могут быть опущены. Однако, должен быть использован как минимум один блок catch или finally .
Try — это выражение
try является выражением, что означает, что оно может иметь возвращаемое значение.
val a: Int? = try < input.toInt() >catch (e: NumberFormatException)
Возвращаемым значением будет либо последнее выражение в блоке try , либо последнее выражение в блоке catch (или блоках). Содержимое finally блока никак не повлияет на результат try -выражения.
Проверяемые исключения
В Kotlin нет проверяемых исключений. Для этого существует целый ряд причин, но мы рассмотрим простой пример, который иллюстрирует причину этого.
Приведённый ниже фрагмент кода является примером простого интерфейса в JDK, который реализован в классе StringBuilder .
Appendable append(CharSequence csq) throws IOException;
Сигнатура говорит, что каждый раз, когда я присоединяю строку к чему-то (к StringBuilder , какому-нибудь логу, сообщению в консоль и т.п), мне необходимо отлавливать исключения типа IOExceptions . Почему? Потому, что данная операция может вызывать IO (Input-Output: Ввод-Вывод) ( Writer также реализует интерфейс Appendable ). Данный факт постоянно приводит к написанию подобного кода:
И это плохо. См. Effective Java, Item 77: Don’t ignore exceptions (не игнорируйте исключения).
Брюс Эккель как-то сказал о проверяемых исключения:
Examination of small programs leads to the conclusion that requiring exception specifications >could both enhance developer productivity and enhance code quality, but experience with large software projects suggests >a different result – decreased productivity and little or no increase in code quality. —>
Анализ небольших программ показал, что обязательная обработка исключений может повысить производительность разработчика и улучшить качество кода. Однако, изучение крупных проектов по разработке программного обеспечения позволяет сделать противоположный вывод: происходит понижение продуктивности и сравнительно небольшое улучшение кода (а иногда и без всякого улучшения).
Вот несколько других рассуждений по этому поводу:
Если вы хотите предупредить вызывающие объекты о возможных исключениях при вызове Kotlin кода из Java, Swift или Objective-C, вы можете использовать аннотацию @Throws . Узнайте больше об использовании этой аннотации для Java, а также для Swift и Objective-C.
Тип Nothing
throw в Kotlin является выражением, поэтому есть возможность использовать его, например, в качестве части Elvis-выражения:
val s = person.name ?: throw IllegalArgumentException("Name required")
Типом выражения throw является специальный тип под названием Nothing . У этого типа нет никаких значений, он используется для того, чтобы обозначить те участки кода, которые могут быть не достигнуты никогда. В своём коде вы можете использовать Nothing для того, чтобы отметить функцию, чей результат никогда не будет возвращён.
fun fail(message: String): Nothing
При вызове такой функции компилятор будет в курсе, что исполнения кода далее не последует:
val s = person.name ?: fail("Name required") println(s) // известно, что переменная 's' проинициализирована к этому моменту
Вы также можете столкнуться с этим типом при работе с выводом типов. Nullable-вариант этого типа Nothing? имеет ровно одно возможное значение — null . Если вы используете null для инициализации значения предполагаемого типа, и нет никакой другой информации, которую можно использовать для определения более конкретного типа, компилятор определит тип Nothing? .
val x = null // у 'x' тип `Nothing?` val l = listOf(null) // у 'l' тип `List
Совместимость с Java
См. раздел, посвящённый исключениям, Страница совместимости Java для получения информации о совместимости с Java.
Exceptions
All exception classes in Kotlin inherit the Throwable class. Every exception has a message, a stack trace, and an optional cause.
To throw an exception object, use the throw expression:
To catch an exception, use the try . catch expression:
There may be zero or more catch blocks, and the finally block may be omitted. However, at least one catch or finally block is required.
Try is an expression
try is an expression, which means it can have a return value:
The returned value of a try expression is either the last expression in the try block or the last expression in the catch block (or blocks). The contents of the finally block don’t affect the result of the expression.
Checked exceptions
Kotlin does not have checked exceptions. There are many reasons for this, but we will provide a simple example that illustrates why it is the case.
The following is an example interface from the JDK implemented by the StringBuilder class:
This signature says that every time I append a string to something (a StringBuilder , some kind of a log, a console, etc.), I have to catch the IOExceptions . Why? Because the implementation might be performing IO operations ( Writer also implements Appendable ). The result is code like this all over the place:
And that’s not good. Just take a look at Effective Java, 3rd Edition, Item 77: Don’t ignore exceptions.
Bruce Eckel says this about checked exceptions:
Examination of small programs leads to the conclusion that requiring exception specifications could both enhance developer productivity and enhance code quality, but experience with large software projects suggests a different result – decreased productivity and little or no increase in code quality.
And here are some additional thoughts on the matter:
If you want to alert callers about possible exceptions when calling Kotlin code from Java, Swift, or Objective-C, you can use the @Throws annotation. Read more about using this annotation for Java and for Swift and Objective-C.
The Nothing type
throw is an expression in Kotlin, so you can use it, for example, as part of an Elvis expression:
The throw expression has the type Nothing . This type has no values and is used to mark code locations that can never be reached. In your own code, you can use Nothing to mark a function that never returns:
When you call this function, the compiler will know that the execution doesn’t continue beyond the call:
val s = person.name ?: fail(«Name required») println(s) // ‘s’ is known to be initialized at this point
You may also encounter this type when dealing with type inference. The nullable variant of this type, Nothing? , has exactly one possible value, which is null . If you use null to initialize a value of an inferred type and there’s no other information that can be used to determine a more specific type, the compiler will infer the Nothing? type:
Java interoperability
Please see the section on exceptions in the Java interoperability page for information about Java interoperability.
Exception Handling in Kotlin
As a seasoned developer, you’re likely already familiar with Spring. But Kotlin can take your developer experience with Spring to the next level!
- Add new functionality to existing classes with Kotlin extension functions.
- Use Kotlin bean definition DSL.
- Better configure your application using lateinit.
- Use sequences and default argument values to write more expressive code.
By the end of this talk, you’ll have a deeper understanding of the advanced Kotlin techniques that are available to you as a Spring developer, and be able to use them effectively in your projects.
1. Overview
In this tutorial, we’ll discuss exception handling in Kotlin.
2. Exceptions
Exceptions are problems that occur during the program execution and disrupt the conventional flow. This can occur due to various reasons like invalid arithmetic operation, a reference to a null object.
Exception handling is the technique to gracefully handle such problems and continue the program execution.
3. Kotlin Exceptions
In Kotlin, there are only unchecked exceptions that are thrown during the runtime execution of the program. All exception classes descend from the class Throwable. Kotlin uses the throw keyword to throw an exception object.
Although Kotlin inherits the concept of exception from Java, it doesn’t support checked exceptions like Java.
The checked exceptions are considered a controversial feature in Java. It decreases developer productivity without any additional increase in code quality. Among other issues, checked exceptions also lead to boilerplate code, difficulty while using lambda expressions.
So, like many other modern programming languages, the Kotlin developers also decided against including checked exceptions as a language feature.
4. Try-Catch Block
We can use the try-catch block for exception handling in Kotlin. In particular, the code that can throw an exception is put inside the try block. Additionally, the corresponding catch block is used to handle the exception.
As a matter of fact, the try block is always followed by a catch or finally block or both of them.
Let’s take a glance at a try-catch block:
try < val message = "Welcome to Kotlin Tutorials" message.toInt() >catch (exception: NumberFormatException) < // . >
4.1. Try-Catch Block as an Expression
An expression may be a combination of one or more values, variables, operators, and functions that execute to provide another value. Hence, we can use the try-catch block as an expression in Kotlin.
Furthermore, the return value of the try-catch expression is the last expression of either the try or the catch block. In the event of an exception, the value of the catch block is returned. However, the results of the expression are not affected by the finally block.
Here’s how we can use try-catch as an expression:
val number = try < val message = "Welcome to Kotlin Tutorials" message.toInt() >catch (exception: NumberFormatException) < // . >return number
4.2. Multiple Catch Blocks
We can use multiple catch blocks together with the try block in Kotlin. Particularly, this is often required if we perform different kinds of operations in the try block, which increases the probability of catching multiple exceptions.
Besides, we must order all catch blocks from the most specific to the most general exception. As an example, the catch block for ArithmeticException must precede the catch block for Exception.
Let’s have a look at the way to use multiple catch blocks:
try < val result = 25 / 0 result >catch (exception: NumberFormatException) < // . >catch (exception: ArithmeticException) < // . >catch (exception: Exception) < // . >
4.3. Nested Try-Catch Block
We can use a nested try-catch block where we implement a try-catch block inside another try block. For instance, this can be required when a block of code may throw an exception and within that block of code, another statement may additionally throw an exception.
Let’s see how we can use the nested try-catch block:
try < val firstNumber = 50 / 2 * 0 try < val secondNumber = 100 / firstNumber secondNumber >catch (exception: ArithmeticException) < // . >> catch (exception: NumberFormatException) < // . >
5. Finally Block
We can use the finally block to always execute code no matter whether an exception is handled or not. Besides, we can use the finally block with the try block by omitting the catch block.
Let’s take a look into the finally block:
try < val message = "Welcome to Kotlin Tutorials" message.toInt() >catch (exception: NumberFormatException) < // . >finally < // . >
6. Throw Keyword
We can use the throw keyword in Kotlin to throw a certain exception or a custom exception.
Here’s how we can use the throw keyword in Kotlin:
val message = "Welcome to Kotlin Tutorials" if (message.length > 10) throw IllegalArgumentException("String is invalid") else return message.length
We can also use throw as an expression in Kotlin. For instance, it can be used as a part of the Elvis expression:
val message: String? = null return message?.length ?: throw IllegalArgumentException("String is null")
The throw expression returns a value of type Nothing. This special type has no values and is used to indicate an unreachable code block. In addition, we can also use the Nothing type in a function to indicate that it will always throw an exception:
fun abstractException(message: String): Nothing
7. Throws Annotation
We can use the @Throws annotation to provide interoperability between Kotlin and Java. Since Kotlin doesn’t have checked exceptions, it doesn’t declare exceptions that are thrown. Let’s define a function in Kotlin:
Now we can call the Kotlin function from Java and catch the exception. Here’s how we can write a try-catch in Java:
The Java compiler will display an error message because the Kotlin function didn’t declare the exception. In such cases, we can use the @Throws annotation in Kotlin to handle the error:
@Throws(IOException::class) fun readFile(): String?
8. Conclusion
In this tutorial, we discussed the various ways of exception handling in Kotlin.
As always, the code for these examples is available over on GitHub.
To learn more about Kotlin features, have a glance at one of our Kotlin tutorials.