Skip to content

リフレクション

_リフレクション_とは、実行時にプログラムの構造を内部調査することを可能にする、言語およびライブラリの機能セットです。 Kotlinでは、関数とプロパティは第一級オブジェクトであり、それらを内部調査する機能(例えば、実行時にプロパティや関数の名前や型を知る機能)は、関数型またはリアクティブスタイルを使用する際に不可欠です。

NOTE

Kotlin/JSは、リフレクション機能に対して制限されたサポートを提供します。Kotlin/JSにおけるリフレクションの詳細はこちら

JVMの依存関係

JVMプラットフォームでは、Kotlinコンパイラの配布物には、リフレクション機能を使用するために必要なランタイムコンポーネントが、kotlin-reflect.jarという別のアーティファクトとして含まれています。これは、リフレクション機能を使用しないアプリケーションのために、ランタイムライブラリの必要なサイズを削減するために行われます。

GradleまたはMavenプロジェクトでリフレクションを使用するには、kotlin-reflectへの依存関係を追加します。

  • Gradleの場合:

kotlin
    dependencies {
        implementation(kotlin("reflect"))
    }
    ```
```groovy [Groovy]
    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-reflect:2.1.21"
    }
    ```
:::

*   Mavenの場合:
    
    ```xml
    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
    </dependencies>
    ```

GradleまたはMavenを使用しない場合は、プロジェクトのクラスパスに`kotlin-reflect.jar`があることを確認してください。
その他のサポートされているケース(コマンドラインコンパイラまたはAntを使用するIntelliJ IDEAプロジェクト)では、デフォルトで追加されます。
コマンドラインコンパイラとAntでは、`-no-reflect`コンパイラオプションを使用して、`kotlin-reflect.jar`をクラスパスから除外できます。

## クラス参照

最も基本的なリフレクション機能は、Kotlinクラスへの実行時参照を取得することです。静的に既知のKotlinクラスへの参照を取得するには、_クラスリテラル_構文を使用できます。

```kotlin
val c = MyClass::class

この参照は、KClass型の値です。

NOTE

JVMでは、Kotlinクラス参照はJavaクラス参照と同じではありません。Javaクラス参照を取得するには、KClassインスタンスの.javaプロパティを使用します。

バウンドクラス参照

特定のオブジェクトのクラスへの参照は、オブジェクトをレシーバとして使用する同じ::class構文で取得できます。

kotlin
val widget: Widget = ...
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }

レシーバ式の型(Widget)に関係なく、オブジェクトの正確なクラス(例えば、GoodWidgetまたはBadWidget)への参照を取得します。

呼び出し可能参照

関数、プロパティ、コンストラクタへの参照は、関数型のインスタンスとしても呼び出したり使用したりできます。

すべての呼び出し可能参照の共通スーパータイプは、KCallable<out R>です。ここでRは戻り値の型です。プロパティの場合はプロパティ型、コンストラクタの場合は構築された型です。

関数参照

以下のように宣言された名前付き関数がある場合、それを直接呼び出すことができます(isOdd(5))。

kotlin
fun isOdd(x: Int) = x % 2 != 0

あるいは、関数を関数型の値として使用できます。つまり、別の関数に渡すことができます。そうするには、::演算子を使用します。

kotlin
fun isOdd(x: Int) = x % 2 != 0

fun main() {
    val numbers = listOf(1, 2, 3)
    println(numbers.filter(::isOdd))
}

ここで::isOddは、関数型(Int) -> Booleanの値です。

関数参照は、パラメータの数に応じて、KFunction<out R>のサブタイプの一つに属します。例えば、KFunction3<T1, T2, T3, R>です。

期待される型がコンテキストからわかっている場合、オーバーロードされた関数でも::を使用できます。 例えば:

kotlin
fun main() {
    fun isOdd(x: Int) = x % 2 != 0
    fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"
    
    val numbers = listOf(1, 2, 3)
    println(numbers.filter(::isOdd)) // refers to isOdd(x: Int)
}

あるいは、明示的に指定された型を持つ変数にメソッド参照を格納することで、必要なコンテキストを提供できます。

kotlin
val predicate: (String) -> Boolean = ::isOdd   // refers to isOdd(x: String)

クラスのメンバーまたは拡張関数を使用する必要がある場合は、String::toCharArrayのように修飾する必要があります。

拡張関数への参照で変数を初期化した場合でも、推論された関数型にはレシーバがありませんが、レシーバオブジェクトを受け入れる追加のパラメータがあります。代わりにレシーバを持つ関数型にするには、型を明示的に指定します。

kotlin
val isEmptyStringList: List<String>.() -> Boolean = List<String>::isEmpty

例: 関数合成

以下の関数を考えてみましょう。

kotlin
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

これは、渡された2つの関数の合成を返します: compose(f, g) = f(g(*))。 この関数を呼び出し可能参照に適用できます。

kotlin
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

fun isOdd(x: Int) = x % 2 != 0

fun main() {
    fun length(s: String) = s.length
    
    val oddLength = compose(::isOdd, ::length)
    val strings = listOf("a", "ab", "abc")
    
    println(strings.filter(oddLength))
}

プロパティ参照

Kotlinでプロパティを第一級オブジェクトとしてアクセスするには、::演算子を使用します。

kotlin
val x = 1

fun main() {
    println(::x.get())
    println(::x.name) 
}

::xは、KProperty0<Int>型のプロパティオブジェクトに評価されます。get()を使用してその値を読み取ったり、nameプロパティを使用してプロパティ名を取得したりできます。詳細については、KPropertyクラスに関するドキュメントを参照してください。

var y = 1のような可変プロパティの場合、::yKMutableProperty0<Int>型の値を返します。この型にはset()メソッドがあります。

kotlin
var y = 1

fun main() {
    ::y.set(2)
    println(y)
}

プロパティ参照は、単一のジェネリックパラメータを持つ関数が期待される場所で使用できます。

kotlin
fun main() {
    val strs = listOf("a", "bc", "def")
    println(strs.map(String::length))
}

クラスのメンバーであるプロパティにアクセスするには、次のように修飾します。

kotlin
fun main() {
    class A(val p: Int)
    val prop = A::p
    println(prop.get(A(1)))
}

拡張プロパティの場合:

kotlin
val String.lastChar: Char
    get() = this[length - 1]

fun main() {
    println(String::lastChar.get("abc"))
}

Javaリフレクションとの相互運用性

JVMプラットフォームでは、標準ライブラリには、Javaリフレクションオブジェクトとのマッピングを提供するリフレクションクラスの拡張が含まれています(kotlin.reflect.jvmパッケージを参照)。 例えば、Kotlinプロパティのバッキングフィールドやゲッターとして機能するJavaメソッドを見つけるには、次のように記述できます。

kotlin
import kotlin.reflect.jvm.*
 
class A(val p: Int)
 
fun main() {
    println(A::p.javaGetter) // prints "public final int A.getP()"
    println(A::p.javaField)  // prints "private final int A.p"
}

Javaクラスに対応するKotlinクラスを取得するには、.kotlin拡張プロパティを使用します。

kotlin
fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin

コンストラクタ参照

コンストラクタは、メソッドやプロパティと同様に参照できます。プログラムがコンストラクタと同じパラメータを受け取り、適切な型のオブジェクトを返す関数型オブジェクトを期待する場所であればどこでも、これらを使用できます。 コンストラクタは、::演算子を使用し、クラス名を追加することで参照されます。パラメータがなく、戻り値の型がFooである関数パラメータを期待する次の関数を考えてみましょう。

kotlin
class Foo

fun function(factory: () -> Foo) {
    val x: Foo = factory()
}

Fooクラスの引数なしコンストラクタである::Fooを使用して、次のように呼び出すことができます。

kotlin
function(::Foo)

コンストラクタへの呼び出し可能参照は、パラメータの数に応じて、KFunction<out R>のサブタイプの一つとして型付けされます。

バウンド関数およびプロパティ参照

特定のオブジェクトのインスタンスメソッドを参照できます。

kotlin
fun main() {
    val numberRegex = "\\d+".toRegex()
    println(numberRegex.matches("29"))
     
    val isNumber = numberRegex::matches
    println(isNumber("29"))
}

matchesメソッドを直接呼び出す代わりに、例ではその参照を使用しています。 このような参照は、そのレシーバにバインドされています。 直接呼び出す(上記の例のように)ことも、関数型式が期待される場所であればいつでも使用することもできます。

kotlin
fun main() {
    val numberRegex = "\\d+".toRegex()
    val strings = listOf("abc", "124", "a70")
    println(strings.filter(numberRegex::matches))
}

バウンド参照とアンバウンド参照の型を比較してください。 バウンド呼び出し可能参照はレシーバが「アタッチ」されているため、レシーバの型はもはやパラメータではありません。

kotlin
val isNumber: (CharSequence) -> Boolean = numberRegex::matches

val matches: (Regex, CharSequence) -> Boolean = Regex::matches

プロパティ参照もバインドできます。

kotlin
fun main() {
    val prop = "abc"::length
    println(prop.get())
}

thisをレシーバとして指定する必要はありません。this::foo::fooは等価です。

バウンドコンストラクタ参照

インナークラスのコンストラクタへのバウンド呼び出し可能参照は、外側のクラスのインスタンスを提供することで取得できます。

kotlin
class Outer {
    inner class Inner
}

val o = Outer()
val boundInnerCtor = o::Inner