Найти тему

Try-with-resources в Kotlin

Оглавление

1. Введение

Управляемые языки, такие как те, которые ориентированы на JVM, автоматически обрабатывают наиболее распространенный ресурс: память.

Однако нам нужно иметь дело со всеми видами ресурсов, а не только с памятью: файлами, сетевыми подключениями, потоками, окнами и т.д. И, к
ак и память, они должны быть освобождены, когда больше не нужны.

2. Автоматическое управление ресурсами

Мы можем выделить три различных этапа при работе с ресурсами в Java (псевдокод).:

resource = acquireResource()
try {
useResource(resource)
} finally {
releaseResource(resource)
}

Если за освобождение ресурса отвечает язык или библиотека (заключительная часть), то мы называем это автоматическим управлением ресурсами. Такая функция избавляет нас от необходимости помнить о необходимости освобождения ресурса.

Кроме того, поскольку управление ресурсами обычно привязано к области действия блока, если мы имеем дело с несколькими ресурсами одновременно, они всегда будут освобождаться в правильном порядке.

В Java объекты, которые содержат ресурс и имеют право на автоматическое управление ресурсами, реализуют определенный интерфейс: закрываемый для ресурсов, связанных с вводом-выводом, и автоматически закрываемый.

Кроме того, в Java 7 был обновлен ранее существовавший интерфейс Closeable, чтобы расширить его возможности автоматического закрытия.

Таким образом, в Kotlin используется та же концепция владельцев ресурсов, то есть объекты, реализующие возможность закрытия или автоматического закрытия.

3. Функция использования в Kotlin

Для автоматического управления ресурсами в некоторых языках есть специальная конструкция: например, в Java 7 появилась функция try-with-resources, в то время как в C# есть ключевое слово using.

Иногда они предлагают нам шаблон, как RAII в C++. В некоторых других случаях они предоставляют нам библиотечный метод.

Kotlin относится ко второй категории.

По замыслу,
в нем нет языковой конструкции, подобной try-with-resources в Java.

Вместо этого мы можем найти метод расширения use в его стандартной библиотеке.

Мы рассмотрим это подробнее позже. На данный момент нам просто нужно знать, что у каждого объекта-владельца ресурса есть метод use, который мы можем вызвать.

3.1. Как им пользоваться

Простой пример:

val writer = FileWriter("test.txt")
writer.use {
writer.write("something")
}

Мы можем вызвать функцию use для любого объекта, который реализует AutoCloseable или закрываемый, точно так же, как в случае с try-with-resources в Java.

Метод принимает лямбда-выражение, выполняет его и удаляет ресурс of (вызывая для него функцию close()) всякий раз, когда выполнение выходит из блока, как обычно, так и в виде исключения.

Таким образом, в этом случае после использования программа записи больше не используется, поскольку Kotlin автоматически закрыл ее.

3.2. Сокращенная форма

В приведенном выше примере для наглядности мы использовали переменную writer, создав таким образом замыкание.

Однако use принимает лямбда–выражение с единственным параметром - объектом, содержащим ресурс:

FileWriter("test.txt")
.use { w -> w.write("something") }

Внутри блока мы также можем использовать неявную переменную it:

FileWriter("test.txt")
.use { it.write("something") }

Итак, как мы видим, нам не нужно давать объекту явное имя. Однако, как правило, лучше быть понятным, а не писать слишком сжатый код.

3.3. Определение use()

Давайте посмотрим на определение функции use в Kotlin, которое можно найти в его стандартной библиотеке:

public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R

В части <T : Closeable?, R> мы видим, что use определяется как функция расширения в интерфейсе Java Closeable.

Подробнее о методах расширения можно прочитать в нашей вводной статье.

Конечно, функция use задокументирована как часть стандартной библиотеки Kotlin.

3.4. Закрываемый или автоматически закрываемый

Если мы обратим более пристальное внимание на пример из предыдущего раздела, то увидим, что сигнатура функции use определена только в интерфейсе Closeable. Это связано с тем, что стандартная библиотека Kotlin ориентирована на Java 6.

В версиях Java до 7 AutoCloseable не существовало, и, конечно, Closeable не расширял его.

На практике классы, реализующие AutoCloseable, но не Closeable, встречаются редко. Тем не менее, мы можем столкнуться с одним из них.

В этом случае нам нужно только добавить зависимость от расширений Kotlin для Java 7, 8 или любой другой версии, на которую мы ориентируемся:

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

Последнюю версию зависимости можно найти на Maven Central.

Это дает нам еще одну функцию расширения использования, определенную в автоматически закрываемом интерфейсе:

public inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R

4. Заключение

В этом руководстве мы увидели, что простая функция расширения в стандартной библиотеке Kotlin - это все, что нам нужно для автоматического управления всеми видами ресурсов, известными JVM.

Оригинал статьи: https://www.baeldung.com/kotlin/try-with-resources