0Pricing
Kotlin Academy · レッスン

実践的なリフレクション

アノテーションを動的に読み取ります

「実践的なリフレクション」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

アノテーションとリフレクション

RUNTIME保持のアノテーションは、実行時にリフレクションで見つけられます。この組み合わせは、シリアライザー、バリデーター、依存性注入を支えています。

このレッスンでは、独自のアノテーションを動的に読み取ります。

クラスのアノテーションを読み取る

すべてのKClassはannotationsリストを公開します。このリストを調べると、特定の型のアノテーションを見つけられます。

@Retention(AnnotationRetention.RUNTIME)
annotation class Entity(val table: String)

@Entity("users")
class User

fun main() {
    val ann = User::class.annotations
        .filterIsInstance<Entity>()
        .first()
    println(ann.table)
}

findAnnotationヘルパー

findAnnotation拡張関数を使うと、手動でフィルタリングするより簡潔に記述できます。アノテーション、またはnullを返します。

import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Entity(val table: String)

@Entity("orders")
class Order

fun main() {
    val ann = Order::class.findAnnotation<Entity>()
    println(ann?.table)
}

プロパティのアノテーション

プロパティは独自のアノテーションを持てます。memberPropertiesを反復処理し、それぞれのプロパティのannotationsを調べます。

import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Column(val name: String)

class Product(@Column("product_id") val id: Int, val title: String)

fun main() {
    Product::class.memberProperties.forEach { prop ->
        val col = prop.findAnnotation<Column>()
        if (col != null) println(prop.name + " -> " + col.name)
    }
}

ミニシリアライザーの構築

プロパティの読み取りとアノテーションを組み合わせて、出力を生成します。ここでは、カラム名が指定されている場合はそれを使い、各プロパティをkey=value形式の文字列に変換します。

import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Column(val name: String)

class Item(@Column("item_id") val id: Int, val name: String)

fun main() {
    val item = Item(7, "Book")
    val parts = Item::class.memberProperties.map { p ->
        val key = p.findAnnotation<Column>()?.name ?: p.name
        key + "=" + p.getter.call(item)
    }
    println(parts.joinToString(", "))
}

マーカーでスキップする

マーカーアノテーションを使うと、無視するプロパティを示せます。hasAnnotationでその存在を確認します。

import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.hasAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Transient2

class Account(val id: Int, @Transient2 val secret: String)

fun main() {
    Account::class.memberProperties
        .filterNot { it.hasAnnotation<Transient2>() }
        .forEach { println(it.name) }
}

アノテーション引数の読み取り

アノテーションのインスタンスを取得したら、そのパラメーターは通常のプロパティです。フィールドと同じように読み取れます。

import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Validated(val min: Int, val max: Int)

@Validated(min = 1, max = 100)
class Quantity

fun main() {
    val v = Quantity::class.findAnnotation<Validated>()!!
    println("range " + v.min + ".." + v.max)
}

関数のアノテーション

関数にもアノテーションを付けて調べることができます。テストランナーがテストメソッドを見つけるように、フレームワークはこれを使ってハンドラーを見つけます。

import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Route(val path: String)

class Api {
    @Route("/home")
    fun home() = "home page"
}

fun main() {
    Api::class.memberFunctions.forEach { f ->
        f.findAnnotation<Route>()?.let { println(f.name + " @ " + it.path) }
    }
}

動作を実行する

ルートアノテーションを読み取り、実際に呼び出しを振り分けます。これがルーティングフレームワークの動作の本質です。

import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.findAnnotation

@Retention(AnnotationRetention.RUNTIME)
annotation class Route(val path: String)

class Api {
    @Route("/ping")
    fun ping() = "pong"
}

fun main() {
    val api = Api()
    val target = "/ping"
    Api::class.memberFunctions.forEach { f ->
        if (f.findAnnotation<Route>()?.path == target) {
            println(f.call(api))
        }
    }
}

実際の用途

アノテーションを利用したリフレクションは、多くのライブラリの基盤になっています。

  • JSONシリアライズでフィールドをキーにマッピングする
  • 制約を適用するバリデーション
  • パスをハンドラーに対応付けるルーティング
  • コンポーネントを接続する依存性注入

対象を絞って使う

リフレクションは、セットアップやフレームワークコードなど、1回またはまれにしか実行されない処理に限定するのが最適です。可能な場合は結果をキャッシュしてください。リクエストごとのホットパスでは、コンパイル時処理を優先してください。

クイックチェック

実践的なリフレクションについて理解度を確認しましょう。

振り返り

アノテーションとリフレクションを組み合わせて、次のことができるようになりました。

  • クラス、プロパティ、関数のアノテーションを見つける
  • 実行時にアノテーションの引数を読み取る
  • ミニシリアライザーとルートディスパッチャーを構築する
  • マーカーアノテーションが付いたフィールドをスキップする

実際のフレームワークはこのように構築されています。

よくある質問

「実践的なリフレクション」レッスンは無料ですか?

はい。「実践的なリフレクション」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「実践的なリフレクション」で何を学びますか?

アノテーションを動的に読み取ります ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「実践的なリフレクション」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. アノテーションの使用
  2. アノテーションの定義
  3. リフレクションの基礎
  4. 実践的なリフレクション
← Kotlin Academyに戻る