Kotlinの拡張関数のuseについて

採用はこちら

Kotlinの拡張関数 use は、I/O処理やデータベースアクセスなど、「リソースを必ず解放しなければならない処理」 を安全かつ簡潔に書くための重要な仕組みです。

一見すると単なる便利関数に見えますが、その背景には 例外処理・リソース管理・JVMとの互換性 といった、実務上かなり重要な設計意図があります。

本記事では、use

  • 仕組みから正確に理解し
  • Javaとの違いを把握し
  • 安全に使える場面・注意すべき場面を区別できる

状態になることを目標に解説します。

目次

use とは何か

useCloseable / AutoCloseable を実装したオブジェクトに対して使える拡張関数 です。

役割は明確で、

処理が終わったら必ず close() を呼ぶことを保証する

という一点に集約されます。

  • 正常終了しても
  • 途中で例外が発生しても

必ずリソースが解放される ことが保証されます。

なぜ use が必要なのか(Javaとの比較)

Javaの場合:try-with-resources

Javaでは、リソース管理は try-with-resources が標準です。

try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line = br.readLine();
}

この構文のポイントは

  • スコープを抜けると自動で close() が呼ばれる
  • 例外が発生しても close() は保証される

Kotlinで同じことを素直に書くと

val reader = BufferedReader(FileReader("test.txt"))
val line = reader.readLine()
reader.close()

この書き方には明確な問題があります。

  • close() の書き忘れリスク
  • 例外発生時に close() が実行されない
  • リソースリーク(ファイル・ソケット・DB接続など)

これらを防ぐために Kotlin では use が提供されています

use は拡張関数として提供されている

use は Kotlin 標準ライブラリで定義された 拡張関数 です。

JVM環境では主に次の2系統があります。

  • Closeable.use
  • AutoCloseable.use

そのため、

  • ファイルやストリーム(Closeable)
  • JDBCの Connection / Statement / ResultSet(AutoCloseable)

のどちらにも自然に使えます。

use の内部的な考え方

use の挙動は、概念的には try-finally によるリソース解放 です。

イメージとしては次のような流れになります。

  1. use ブロック内の処理を実行
  2. 正常終了でも例外発生でも
  3. 最後に必ず close() を呼ぶ

ただし、実際の実装は単純な finally { close() } ではありません。

重要なポイント:例外の扱い

  • ブロック内で例外が発生
  • さらに close() の中でも例外が発生

このような場合でも、

  • 元の例外を優先
  • close() 側の例外は suppressed(抑制例外) として扱う

という、Javaの try-with-resources と同等の安全設計がされています。

つまり use は「便利」なだけでなく、例外処理の設計としても非常に慎重に作られているという点が重要です。

基本的な使い方

ファイル読み込み

File("test.txt").bufferedReader().use { reader ->
    val line = reader.readLine()
    println(line)
}
  • use ブロックを抜けた瞬間に close() が呼ばれる
  • 明示的に close() を書く必要はない

ラムダ引数を省略した書き方

File("test.txt").bufferedReader().use {
    println(it.readLine())
}

Kotlinらしく、非常に簡潔に書けます。

use は戻り値を返せる

useブロックの最後の式をそのまま戻り値として返す ことができます。

val text: String = File("test.txt").bufferedReader().use {
    it.readText()
}
  • 戻り値が返された後に close() が実行される
  • 戻り値の安全性は損なわれない

この性質を使うと、関数を非常に短く書けます。

例外が発生した場合の挙動

File("test.txt").bufferedReader().use {
    throw RuntimeException("エラー")
}

この場合

  • close() は必ず実行される
  • 例外はそのまま呼び出し元へ伝播する

リソース解放と例外処理が完全に分離されている
これが use の最大の価値です

use を使う代表的な場面

ファイル・ストリーム

FileInputStream("a.txt").use { stream ->
    // 読み込み処理
}

ソケット通信

Socket("localhost", 8080).use { socket ->
    // 通信処理
}

JDBC(ネストしても安全)

connection.prepareStatement(sql).use { stmt ->
    stmt.executeQuery().use { rs ->
        while (rs.next()) {
            println(rs.getString("name"))
        }
    }
}

ネストは深くなりますが、すべてのリソースが確実に解放される点は非常に強力です。

use を使うべきでないケース

close してはいけない共有リソース

System.`in`.use { ... } // 原則避ける
  • System.in / System.out / System.err
  • アプリ全体で共有されるリソース

これらを use で閉じると、以降の入出力処理が壊れる可能性があります。

ライフサイクルを外で管理するオブジェクト

class Repository(private val connection: Connection) {
    fun findAll() {
        // ここで use すると connection が閉じてしまう
    }
}
  • DIコンテナ
  • アプリケーションスコープの接続
  • プール管理されたリソース

これらは use の責務ではありません

use とスコープ関数の違い

関数主目的
useリソース管理(close保証)
let値の変換・null安全
also副作用処理
apply初期化

use だけは 「必ず close する」前提
他のスコープ関数とは思想がまったく違います

まとめ

  • use は Kotlin における try-with-resources の正統後継
  • Closeable / AutoCloseable を安全に扱える
  • 例外発生時も確実に close() が実行される
  • 戻り値を返せるためコードが簡潔になる
  • 共有リソースや長寿命オブジェクトには使わない

use を正しく理解して使えるかどうかは、Kotlinを「書ける人」と「設計できる人」の分かれ目になります。

以上、Kotlinの拡張関数のuseについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次