1. 概要

このチュートリアルでは、Kotlinアノテーションの概要を説明します。

それらを適用する方法、独自のものを作成およびカスタマイズする方法を示します。 次に、JavaとKotlinでのアノテーションとキーワードの相互作用について簡単に説明します。

最後に、アノテーションを処理する方法を示すクラス検証の簡単な例を示します。

2. 注釈の適用

Kotlinでは、コード要素の前に@記号を前に付けた名前を付けることでアノテーションを適用します。 たとえば、 Positive という名前の注釈を適用する場合は、次のように記述します。

@Positive val amount: Float

多くの場合、注釈にはパラメータがあります。 注釈パラメーターはコンパイル時定数である必要があり、次のタイプである必要があります。

  • Kotlinプリミティブ型( Int、Byte、Short、Float、Double、Char、Boolean
  • 列挙
  • クラス参照
  • 注釈
  • 上記のタイプの配列

注釈にパラメーターが必要な場合は、関数呼び出しのように括弧内にその値を指定します。

@SinceKotlin(version="1.3")

この場合、アノテーションパラメータもアノテーションである場合は、@記号を省略してください。

@Deprecated(message="Use rem(other) instead", replaceWith=ReplaceWith("rem(other)"))

アノテーションパラメータがクラスオブジェクトの場合、クラス名に ::classを追加する必要があります。次に例を示します。

@Throws(IOException::class)

注釈パラメーターが複数の値を持つ可能性があることを指定する必要がある場合は、それらの値の配列を渡すだけです。

@Throws(exceptionClasses=arrayOf(IOException::class, IllegalArgumentException::class))

Kotlin 1.2以降、次の構文も使用できるようになりました。

@Throws(exceptionClasses=[IOException::class, IllegalArgumentException::class])

3. 注釈の宣言

アノテーションを宣言するために、クラスを定義し、annotationキーワードをclassoneの前に配置します。 その性質上、アノテーションの宣言にコードを含めることはできません。

最も単純な注釈にはパラメーターがありません。

annotation class Positive

パラメーターを必要とするアノテーションの宣言は、プライマリコンストラクターを持つクラスのようなものです。

annotation class Prefix(val prefix: String)

カスタムアノテーションを宣言するときは、それらを適用する可能性のあるコード要素と、それらを格納する場所を指定する必要があります。 このメタ情報を定義するために使用される注釈は、メタ注釈と呼ばれます。

次のセクションでは、それらについて簡単に説明します。 最新の情報については、公式ドキュメントをいつでも確認できます。

3.1. @Target

このメタアノテーションは、このアノテーションが参照できるコード要素を指定します。 AnnotationTarget列挙のインスタンスまたはその配列である必要がある必須パラメーターがあります。 したがって、次の要素に注釈を適用するように指定できます。

  • クラス
  • ANNOTATION_CLASS
  • TYPE_PARAMETER
  • 財産
  • 分野
  • LOCAL_VARIABLE
  • VALUE_PARAMETER
  • コンストラクタ
  • 関数
  • PROPERTY_GETTER
  • PROPERTY_SETTER
  • タイプ
  • 表現
  • ファイル
  • TYPEALIAS

明示的に指定しない場合、対応する注釈をデフォルトで次の要素に適用できます。

CLASS、PROPERTY、FIELD、LOCAL_VARIABLE、VALUE_PARAMETER、CONSTRUCTOR、FUNCTION、PROPERTY_GETTER、PROPERTY_SETTER

3.2. @Retention

このメタ注釈は、注釈を .class ファイルに保存するかどうか、およびリフレクションで表示するかどうかを指定します。 必須パラメーターは、次の要素を持つAnnotationRetention列挙のインスタンスである必要があります。

  • ソース
  • バイナリ
  • ランタイム

Javaとは異なり、Kotlinの@Retentionのデフォルト値はRUNTIMEです。

3.3. @Repeatable

@Repeatable は、要素に同じタイプの複数の注釈があるかどうかを指定します。 このメタアノテーションはパラメーターを受け入れません。

3.4. @MustBeDocumented

@MustBeDocumented は、注釈のドキュメントを生成されたドキュメントに含めるかどうかを指定します。 このメタアノテーションはパラメータも受け入れません。

4. Java-アノテーションとの相互運用性

Kotlinは通常、Javaに関してより簡潔です。 たとえば、プロパティを宣言するときのように、追加のメソッドが自動的に作成されます。

val name: String?;

コンパイラは、このプロパティのプライベートフィールドとゲッターおよびセッターを自動的に作成します。 その結果、疑問が生じます。プロパティに注釈を追加すると、どこに適用されるのでしょうか。 ゲッター、セッター、それともフィールド自体に?

Kotlinでは、Javaコードで定義されたアノテーションでプロパティを装飾すると、対応するフィールドに適用されます。

たとえば、JUnitの @Rule アノテーションを使用して、アノテーションでフィールドをパブリックにする必要がある場合、問題が発生する可能性があります。 あいまいさを回避するために、Kotlinにはいわゆる使用サイトターゲット宣言があります。

4.1. 使用-サイトターゲット宣言

使用サイトのターゲットはオプションです。 @ 記号と注釈名の間に、コロン記号を区切り文字として使用して配置します。 この構文では、一度に複数の注釈名を指定できます。

Kotlinフィールドに@get:Positive を配置する場合、アノテーションは実際にそのフィールド用に生成されたゲッターをターゲットにする必要があることを意味します。

Kotlinは、以下に対応する使用サイトターゲットの次の値をサポートします。

  • delegate –委任されたプロパティを格納するフィールド
  • field –プロパティ用に生成されたフィールド
  • file –そのファイルで定義されたトップレベルの関数とプロパティを含むクラス
  • get / set –プロパティゲッター/セッター
  • param –コンストラクターパラメーター
  • property – Kotlinのプロパティであり、Javaコードからはアクセスできません
  • receiver –拡張関数またはプロパティのレシーバーパラメーター

4.2. JVM関連の注釈

次のKotlinアノテーションを使用すると、Javaコードからの使用方法をカスタマイズできます。

  • @JvmName –生成されたJavaメソッドまたはフィールドの名前を変更できます
  • @JvmStatic –生成されたJavaメソッドまたはフィールドを静的にするように指定できます
  • @JvmOverloads –Kotlinコンパイラがデフォルトパラメータの代わりにオーバーロード関数を生成する必要があることを示します
  • @JvmField –生成されたJavaフィールドは、ゲッター/セッターのないパブリックフィールドである必要があることを示します

一部のJavaアノテーションはKotlinのキーワードになり、その逆も同様です。

Java Kotlin
@オーバーライド オーバーライド
揮発性 @Volatile
strictfp @Strictfp
同期 @synchronized
一時的 @トランジェント
スロー @Throws

5. 注釈の処理

注釈を処理する方法を示すために、簡単なバリデーターを作成しましょう。 ここでは、完全なコードがGithubのリポジトリで利用可能である間、アイデアのみを提示します。

アイテムのインスタンスが有効かどうかを判断する必要があるとします。

class Item(val amount: Float, val name: String)

Item インスタンスは、 amount の値が正で、nameの値がAliceまたはボブ

このために、ItemクラスのプロパティをカスタムアノテーションPositiveおよびAllowedNamesで装飾しましょう。

class Item(
  @Positive val amount: Float, 
  @AllowedNames(["Alice", "Bob"]) val name: String)

単純な実装では、Itemのプロパティを取得するだけです。

val fields = item::class.java.declaredFields
for (field in fields) {...}

各プロパティのすべての注釈を繰り返し処理します。

for (annotation in field.annotations) {...}

Itemのプロパティを装飾したものを見つけるために。

たとえば、次のコマンドを使用して、AllowedNamesがプロパティに存在するかどうかを検出できます。

field.isAnnotationPresent(AllowedNames::class.java)

注釈が表示されると、許可された値と比較するだけで、プロパティに有効な値があるかどうかを簡単に判断できます。

val allowedNames = field.getAnnotation(AllowedNames::class.java)?.names

アノテーションはJavaReflectionAPIを使用することに注意してください。 その結果、アノテーションに大きく依存すると、コードのパフォーマンスが低下する可能性があります。

6. 結論

この記事では、Kotlinアノテーションとそれに対応するJavaについて考察しました。 Kotlinアノテーションを適用する方法、カスタムアノテーションを作成する方法、そしてそれらを処理する方法について説明しました。

ご覧のとおり、KotlinアノテーションはJavaのアノテーションと非常によく似ています。 それでも、チュートリアルJavaでのカスタムアノテーションの作成も役立つ場合があります。

いつものように、完全なコードはGitHubリポジトリから入手できます。