コトリンによる反射

Kotlinによるリフレクション

1. 前書き

リフレクションは、実行時にクラス、フィールド、およびメソッドを検査、ロード、および対話する機能の名前です。 コンパイル時に何であるかわからない場合でも、これを行うことができます。

これには、開発しているものに応じて、多くの用途があります。 たとえば、Springのようなフレームワークはそれを多用します。

このサポートはJVMに組み込まれているため、すべてのJVMベースの言語で暗黙的に利用できます。 ただし、一部のJVM言語では、すでに利用可能なものの上に追加のサポートがあります。

2. Javaリフレクション

All the standard Java Reflection constructs are available and work perfectly well with our Kotlin code。 これには、java.lang.Classクラスと、java.lang.reflectパッケージ内のすべてが含まれます。

何らかの理由で標準のJava Reflection APIを使用する場合は、Javaの場合とまったく同じ方法で使用できます。 たとえば、Kotlinクラスのすべてのパブリックメソッドのリストを取得するには、次のようにします。

MyClass::class.java.methods

これは、次の構成要素に分類されます。

  • MyClass::classは、MyClassクラスのKotlinクラス表現を提供します

  • .javaは、java.lang.Classと同等のものを提供します

  • .methodsは、java.lang.Class.getMethods()アクセサメソッドの呼び出しです。

This will work exactly the same whether called from Java or Kotlin, and whether called on a Java or a Kotlin class。 これには、データクラスなどのKotlin固有の構造が含まれます。

data class ExampleDataClass(
  val name: String, var enabled: Boolean)

ExampleDataClass::class.java.methods.forEach(::println)

Kotlinは、返された型もKotlin表現に変換します。

上記では、forEach().を呼び出すことができるkotlin.Array<Method>を取得します

3. Kotlinリフレクションの機能強化

Whilst we can use the standard Java Reflection APIs, it isn’t aware of all extensions that Kotlin brings to the platform

また、状況によっては使用するのが少し面倒な場合があります。 Kotlinには、これらの問題を解決する独自のリフレクションAPIが用意されています。

Kotlin Reflection APIへのすべてのエントリポイントは、参照を使用します。 以前、クラス定義への参照を与えるために::classを使用することを見ました。 これを使用して、メソッドとプロパティへの参照を取得することもできます。

3.1. Kotlinクラスリファレンス

The Kotlin Reflection API allows access to a Class reference. This can then be used to introspect the full details of the Kotlin class。 これにより、Javaクラス参照(java.lang.Classオブジェクト)にアクセスできるだけでなく、Kotlin固有のすべての詳細にもアクセスできます。

クラス詳細用のKotlinAPIは、kotlin.reflect.KClassクラスを中心にしています。 This can be accessed by using the :: operator from any class name or instance –例: String::class.

または、JavaClassインスタンスが使用可能な場合は、拡張メソッドjava.lang.Class.kotlinを使用してアクセスできます。

val listClass: KClass = List::class

val name = "example"
val stringClass: KClass = name::class

val someClass: Class
val kotlinClass: KClass = someClass.kotlin

Once we have obtained a KClass object, there are some simple things that it can tell us about the class in question。 これらの一部は標準的なJavaの概念であり、その他はKotlin固有の概念です。

たとえば、クラスが抽象クラスか最終クラスかは簡単にわかりますが、クラスがデータクラスかコンパニオンクラスかどうかもわかります。

val stringClass = String::class
assertEquals("kotlin.String", stringClass.qualifiedName)
assertFalse(stringClass.isData)
assertFalse(stringClass.isCompanion)
assertFalse(stringClass.isAbstract)
assertTrue(stringClass.isFinal)
assertFalse(stringClass.isSealed)

クラス階層内を移動する方法もあります。 Javaでは、必要に応じて、クラスからそのスーパークラス、インターフェイス、およびそれが含まれる外部クラスにすでに移動できます。

Kotlinはこれに、任意のクラスのコンパニオンオブジェクトと、オブジェクトクラスのObjectインスタンスを取得する機能を追加します。

println(TestWithCompanion::class.companionObject)
println(TestWithCompanion::class.companionObjectInstance)
println(TestObject::class.objectInstance)

We can create new instances of a class from a Class Reference as well、Javaの場合とほぼ同じ方法:

val listClass = ArrayList::class

val list = listClass.createInstance()
assertTrue(list is ArrayList)

または、必要に応じてコンストラクタにアクセスし、明示的なコンストラクタを使用できます。 これらはすべて、次のセクションで説明するメソッド参照です。

同様の方法で、すべてのメソッド、プロパティ、拡張機能、およびクラスの他のメンバーにアクセスできます。

val bigDecimalClass = BigDecimal::class

println(bigDecimalClass.constructors)
println(bigDecimalClass.functions)
println(bigDecimalClass.memberProperties)
println(bigDecimalClass.memberExtensionFunctions)

3.2. Kotlinメソッドリファレンス

クラス、we can also interact with Methods and Propertiesと対話できることに加えて。

これには、valまたはvarで定義されたクラスプロパティ、標準クラスメソッド、および最上位関数が含まれます。 前と同じように、これは標準Javaで記述されたコードでも、Kotlinで記述されたコードでも同様に機能します。

クラスの場合とまったく同じように、we can obtain a reference to a Method or Property using the:: operator

これは、メソッド参照を取得するためのJava 8の場合とまったく同じに見え、まったく同じ方法で使用できます。 ただし、Kotlinでは、このメソッド参照を使用して、ターゲットに関する反射情報を取得することもできます。

Once we have obtained a method reference, we can call it as if was really the method in question。 これは呼び出し可能リファレンスとして知られています:

val str = "Hello"
val lengthMethod = str::length

assertEquals(5, lengthMethod())

クラスについても同様に、メソッド自体の詳細を取得できます。 これには、標準のJavaの詳細と、メソッドがoperatorであるか、inline:であるかなどのKotlin固有の詳細の両方が含まれます。

val byteInputStream = String::byteInputStream
assertEquals("byteInputStream", byteInputStream.name)
assertFalse(byteInputStream.isSuspend)
assertFalse(byteInputStream.isExternal)
assertTrue(byteInputStream.isInline)
assertFalse(byteInputStream.isOperator)

In addition to this, we can get more information about the inputs and outputs of the method through this reference

これには、null可能性やオプション性などのKotlin固有の詳細を含む、戻り値の型とパラメーターに関する詳細が含まれます。

val str = "Hello"
val method = str::byteInputStream

assertEquals(
  ByteArrayInputStream::class.starProjectedType,
  method.returnType)
assertFalse(method.returnType.isMarkedNullable)

assertEquals(1, method.parameters.size)
assertTrue(method.parameters[0].isOptional)
assertFalse(method.parameters[0].isVararg)
assertEquals(
  Charset::class.starProjectedType,
  method.parameters[0].type)

3.3. Kotlinプロパティリファレンス

This works exactly the same for Properties as wellは、明らかに、取得できる詳細は異なります。 代わりに、プロパティが定数であるか、初期化が遅いか、変更可能かを通知できます。

lateinit var mutableProperty: String
val mProperty = this::mutableProperty
assertEquals("mutableProperty", mProperty.name)
assertTrue(mProperty.isLateinit)
assertFalse(mProperty.isConst)
assertTrue(mProperty is KMutableProperty<*>)

Note that the concept of Properties also works in any non-Kotlin code. These are identified by fields that follow the JavaBeans conventions regarding getter and setter methods

これには、Java標準ライブラリのクラスが含まれます。 たとえば、Throwableクラスには、メソッドgetMessage()が定義されているため、プロパティThrowable.messageがあります。

We can access the actual Property through Method references that are exposedgetterおよびsetterメソッド。 setterは、KMutableProperty –を使用している場合にのみ使用できます。 プロパティはvarとして宣言されましたが、getterは常に使用可能です。

これらは、get()およびset()メソッドを介してより使いやすい方法で公開されます。 gettersetterの値は実際のメソッド参照であり、他のメソッド参照とまったく同じように操作できます。

val prop = this::mutableProperty

assertEquals(
  String::class.starProjectedType,
  prop.getter.returnType)

prop.set("Hello")
assertEquals("Hello", prop.get())

prop.setter("World")
assertEquals("World", prop.getter())

4. 概要

この記事では、標準Java言語に組み込まれているリフレクション機能との相互作用や相違点など、Kotlinでリフレクションを使用して実現できるいくつかの事項の概要を説明します。

すべての例は利用可能なover on GitHubです。