1. 概要

この記事では、チェックされた例外がないことと、Kotlinでのその影響について説明します。 その過程で、@ThrowsアノテーションがJavaからのKotlinメソッドと関数の呼び出しをどのように容易にするかを学びます。 また、プロジェクトでこのアノテーションを使用する必要がある場合と使用しない場合についても学習します。

2. チェックされた例外はありません

Kotlinでは、Java とは対照的に、チェックされた例外などはありません。 したがって、Kotlinのthrows句を使用してJavaチェック例外を宣言する必要はありません。 実際のところ、Kotlinにはthrows句すらありません。

fun throwJavaUnchecked() {
    throw IllegalArgumentException()
}

fun throwJavaChecked() {
    throw IOException()
}

上記の例では、Java標準ライブラリからチェックされた例外とチェックされていない例外をスローしています。 この例から明らかなように、KotlinはJavaの世界での例外のタイプを気にせず、それらすべてを同じように扱います。

Kotlinにはチェックされた例外がないため、メソッドシグネチャで宣言しなくても、さまざまな関数で簡単に例外をスローできます。

3. Javaの相互運用性

ご存知のとおり、Kotlinの最も魅力的な機能の1つは、Javaとの優れた相互運用性です。 ここで、Kotlinが例外をチェックしていないという事実を踏まえて、Javaクラスから上記の関数を呼び出しましょう。

public class Caller {

    public static void main(String[] args) {
        unchecked();
    }

    public static void unchecked() {
        try {
            ThrowsKt.throwJavaUnchecked();
        } catch (IllegalArgumentException e) {
            System.out.println("Caught something!");
        }
    }
}

ここでは、Javaから throwJavaUnchecked()関数( throws.kt ファイルで定義されているため、 ThrowsKt 名)を呼び出して、 IllegalArgumentException例外。 この例外はJavaではチェックされていないものであるため、コードはコンパイルされ、完全に正常に実行されます。 したがって、コンパイルして実行すると、例外がキャッチされ、期待される出力が出力されます。

一方、 throwJavaChecked()関数を呼び出すときに、チェックされた IOException をキャッチしようとすると、コードはコンパイルされません。

public static void checked() {
    try {
        ThrowsKt.throwJavaChecked();
    } catch (IOException e) {
        System.out.println("Won't even compile");
    }
}

ここで、Javaコンパイラは次のエラーメッセージで失敗します。

java: exception java.io.IOException is never thrown in body of corresponding try statement

エラーメッセージは明らかです。呼び出された関数(この場合はthrowJavaChecked)がIOException例外を宣言しなかったが、呼び出し元のメソッドがそれをキャッチしていることを示しています。 Javaでは、 throws 句を使用して宣言せずに、チェックされた例外をキャッチすることはできません。

それでは、この相互運用性の問題に対するKotlinの回答を見てみましょう。

4. @Throwsアノテーション

このJavaの相互運用性の問題を修正するために、Kotlinは@Throwsアノテーションを提供します。 Kotlinメソッドまたは関数に@Throwsアノテーションを付けると、Kotlinはそのメソッドまたは関数をそのシグネチャにthrows句を使用してコンパイルします

@Throws(IOException::class)
fun throwJavaChecked() {
    throw IOException()
}

上に示したように、 throwJavaChecked()関数がIOExceptionをスローすることを宣言しています。 明らかに、この情報はJavaクライアントにのみ有益であり、純粋なKotlinコードベースには何の価値もありません。

現在、Javaクライアントでは、この関数を呼び出すときに、IOExceptionをキャッチする必要があります。

try {
    ThrowsKt.throwJavaChecked();
} catch (IOException e) {
    System.out.println("It works this time!");
}

または、throws句で宣言します。

public static void checked() throws IOException {
    ThrowsKt.throwJavaChecked();
}

したがって、このアノテーションをKotlin関数の上に配置することは、throws句を使用してJavaメソッドを定義することに似ています。 Kotlin 1.4の時点で、@Throwsアノテーションにtypealiasがあることに注意してください。

@SinceKotlin("1.4")
public actual typealias Throws = kotlin.jvm.Throws

したがって、古いバージョンを使用している場合は、注釈を適切にインポートする必要があります。

import kotlin.jvm.Throws

@Throws(IOException::class)
fun throwJavaChecked() {
    throw IOException()
}

4.1. バイトコード表現

アノテーションの使用方法がわかったので、内部でどのように見えるかを見てみましょう。 まず、kotlincを使用してKotlinファイルをコンパイルする必要があります。

>> kotlinc throws.kt

次に、 javap ツールを使用して、生成されたバイトコードを確認できます。

>> javap -c -p com.baeldung.throwsannotation.ThrowsKt
// omitted
public static final void throwJavaChecked() throws java.io.IOException;

上に示したように、Kotlinコンパイラはバイトコードでthrows句を発行します。 これは、@Throwsアノテーションを使用した場合の効果です。 比較のために、他の関数のバイトコードは次のようになります。

public static final void throwJavaUnchecked();

明らかに、ここにはthrows句の兆候はありません。

5. 結論

この記事では、Kotlinにはチェックされた例外などがないことを学びました。 さらに、 @Throws アノテーションを使用して、JavaクライアントからKotlin関数を呼び出すときにチェックされた例外を簡単に使用できるようにしました。

ここまでで、純粋なKotlinプロジェクトにこのアノテーションを使用しても意味がないことも知っておく必要があります。これは、Javaの相互運用性を向上させることが唯一の目的であるためです。

いつものように、すべての例はGitHubから入手できます。