«KFunction», qui est une méthode / constructeur dans le reflet de «Kotlin», a plusieurs parties abstraites et est facile à utiliser depuis l'application.
D'un autre côté, comparé au cas de l'appel direct de Constructor
/ Method
de Java
, KFunction
a un problème que l'appel est lent en raison de la surcharge due à l'abstraction.
Dans cet article, pour éviter cette surcharge, nous allons résumer le modèle pour obtenir le Constructor
/ Method
de Java
et l'appeler directement par la définition de la KFunction
.
Les versions sont respectivement Kotlin 1.4.10
/ Java8
.
Avant d'entrer dans le sujet principal, j'écrirai sur kotlin.reflect.KFunction
et java.lang.reflect.Constructor
/ java.lang.reflect.Method
.
Comme le nom du paquet l'indique, le premier est une fonction dans le reflet de Kotlin
.
Dans «Kotlin», les méthodes et les constructeurs peuvent être traités comme la même «KFunction».
KFunction
peut utiliser l'argument par défaut en appelant callBy
, ou peut ignorer l'argument d'instance dans Method.invoke
de Java
sous certaines conditions.
Ce dernier est une fonction dans la réflexion «Java». Puisque «Constructor» et «Method» sont séparés en tant que types, ils ne peuvent pas être traités de la même manière, et «Method» requiert des paramètres d'instance («null» pour «static method», et les instances autrement).
La raison pour laquelle j'ai écrit au début, "L'appel de KFunction
est plus lent que le cas de l'appel direct de Constructor
/ Method
de Java
" n'utilise pas l'argument par défaut (= tous les paramètres sont alignés) Il s'agit d'une comparaison entre l'appel de KFunction.call
et l'appel de Constructor.newInstance
/ Method.invoke
.
Il existe à peu près deux types de méthodes de définition de fonction et quatre types en détail, dont chacun est résumé.
--Constructeur --Autre que le constructeur
@ JvmStatic
)Le code suivant a été utilisé pour la vérification.
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import kotlin.reflect.KFunction
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.functions
import kotlin.reflect.jvm.javaConstructor
import kotlin.reflect.jvm.javaMethod
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
fun instanceMethodSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
companion object {
fun companionObjectFunctionSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
@JvmStatic
fun staticMethodSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
}
val expected = ConstructorSample(1, "2")
@Test
@DisplayName("Pour les constructeurs")
fun constructorTest() {
val function: KFunction<ConstructorSample> = ::ConstructorSample
assertEquals(expected, function.call(1, "2"))
val javaConstructor: Constructor<ConstructorSample> = function.javaConstructor!!
assertEquals(expected, javaConstructor.newInstance(1, "2"))
}
@Test
@DisplayName("Par exemple les fonctions")
fun instanceMethodTest() {
val function: KFunction<ConstructorSample> = this::instanceMethodSample
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(this, 1, "2"))
}
@Nested
@DisplayName("Pour les méthodes définies sur les objets compagnons")
inner class CompanionObjectFunctionTest {
@Test
@DisplayName("Lorsque obtenu par référence de méthode")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::companionObjectFunctionSample
//Aucun paramètre d'instance requis lors de la récupération avec la référence de méthode
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Une fois acquis par réflexion")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "companionObjectFunctionSample" }
.let { it as KFunction<ConstructorSample> }
//Paramètres d'instance requis lors de l'acquisition par réflexion
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
@Nested
@DisplayName("Dans le cas de la méthode définie dans l'objet compagnon (avec JvmStatic spécifié)")
inner class StaticMethodTest {
@Test
@DisplayName("Lorsque obtenu par référence de méthode")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::staticMethodSample
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Lorsqu'elle est obtenue par réflexion d'un objet compagnon")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "staticMethodSample" }
.let { it as KFunction<ConstructorSample> }
//Paramètres d'instance requis lors de l'acquisition par réflexion
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
}
Si la source de KFunction
est un constructeur, vous pouvez obtenirConstructor
avec KFunction.javaConstructor
.
Puisque le constructeur ne requiert aucun paramètre d'instance, etc., vous pouvez appeler Constructor.newInstance
de la même manière que KFunction.call
.
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
val expected = ConstructorSample(1, "2")
@Test
@DisplayName("Pour les constructeurs")
fun constructorTest() {
val function: KFunction<ConstructorSample> = ::ConstructorSample
assertEquals(expected, function.call(1, "2"))
val javaConstructor: Constructor<ConstructorSample> = function.javaConstructor!!
assertEquals(expected, javaConstructor.newInstance(1, "2"))
}
}
Si la source de KFunction
est autre que le constructeur, vous pouvez obtenirMethod
avec KFunction.javaMethod
.
Comme mentionné ci-dessus, vous avez besoin d'un paramètre d'instance («null» pour «méthode statique», sinon une instance) pour appeler «Method».
Ici, dans les cas autres que le constructeur, la méthode d'acquisition d'une instance de «KFunction» n'est pas divulguée quelle que soit la méthode de génération, donc même si le paramètre d'instance est omis dans «KFunction.call», de «KFunction» uniquement. Vous ne pouvez pas appeler Method.invoke
[^ instance_hosoku].
Même si vous ajoutez «JvmStatic», vous ne pouvez pas appeler «Method.invoke» avec le paramètre d'instance comme «null» car c'est la définition de «l'objet compagnon» qui peut être acquise par la méthode d'acquisition en tant que «KFunction».
[^ instance_hosoku]: Tant que le paramètre d'instance peut être omis dans KFunction.call
, on considère que les informations d'instance sont conservées en interne d'une certaine manière, mais elles sont exposées dans l'interface de KFunction
. Puisque KFunctionImpl
, qui est une classe d'implémentation de KFunction
, est une classe ʻinternal`, il est jugé que si vous la poursuivez plus loin, cela deviendra une véritable magie noire, et ici nous concluons que cela ne peut être fait.
Par conséquent, afin d'appeler «Method» à partir de «KFunction» obtenue à partir d'un autre constructeur que le constructeur, il est nécessaire de préparer une instance séparément.
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
fun instanceMethodSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
val expected = ConstructorSample(1, "2")
@Test
@DisplayName("Par exemple les fonctions")
fun instanceMethodTest() {
val function: KFunction<ConstructorSample> = this::instanceMethodSample
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(this, 1, "2"))
}
}
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
companion object {
fun companionObjectFunctionSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
}
val expected = ConstructorSample(1, "2")
@Nested
@DisplayName("Pour les méthodes définies sur les objets compagnons")
inner class CompanionObjectFunctionTest {
@Test
@DisplayName("Lorsque obtenu par référence de méthode")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::companionObjectFunctionSample
//Aucun paramètre d'instance requis lors de la récupération avec la référence de méthode
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Une fois acquis par réflexion")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "companionObjectFunctionSample" }
.let { it as KFunction<ConstructorSample> }
//Paramètres d'instance requis lors de l'acquisition par réflexion
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
}
@ JvmStatic
) [^ static_hosoku][^ static_hosoku]: Comme résumé dans cet article, les fonctions définies sur Kotlin
ne peuvent pas être obtenues par static Functions
, donc dans cet exemple Il est omis.
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
companion object {
@JvmStatic
fun staticMethodSample(foo: Int, bar: String): ConstructorSample = ConstructorSample(foo, bar)
}
val expected = ConstructorSample(1, "2")
@Nested
@DisplayName("Dans le cas de la méthode définie dans l'objet compagnon (avec JvmStatic spécifié)")
inner class StaticMethodTest {
@Test
@DisplayName("Lorsque obtenu par référence de méthode")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::staticMethodSample
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Lorsqu'elle est obtenue par réflexion d'un objet compagnon")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "staticMethodSample" }
.let { it as KFunction<ConstructorSample> }
//Paramètres d'instance requis lors de l'acquisition par réflexion
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
}
Dans cet article, afin d'éviter la surcharge d'appel de KFunction
, nous avons résumé le modèle pour obtenirConstructor
/ Method
of Java
par la définition de KFunction
et l'appelons directement.
À la suite de la vérification, il a été conclu que dans le cas d'un constructeur, Constructor.newInstance
peut être appelé à partir d'une seule KFunction
, et dans d'autres cas, Method.invoke
ne peut être appelé que si l'instance peut être obtenue séparément. J'ai fait.
Il semble qu'il soit plus facile d'utiliser KFunction.callBy
si vous voulez juste créer un outil qui fonctionne, mais si vous voulez créer un outil quelle que soit la vitesse d'exécution, vous pouvez appeler Constructor
/ Method
directement. Vous pouvez l'essayer.
J'espère que cet article vous a aidé.