KFunction
, eine Methode / Konstruktor in der Reflexion von Kotlin
, hat verschiedene Teile abstrahiert und ist einfach aus der Anwendung zu verwenden.
Andererseits hat KFunction im Vergleich zum direkten Aufrufen von "Constructor" / "Method" von "Java" das Problem, dass der Aufruf aufgrund des Abstraktionsaufwands langsamer ist.
Um diesen Overhead zu vermeiden, werden wir in diesem Artikel das Muster zusammenfassen, wie der "Konstruktor" / "Methode" des "Java" abgerufen und direkt durch die Definition der "KFunction" aufgerufen wird. Die Versionen sind "Kotlin 1.4.10" / "Java8".
Bevor ich zum Hauptthema komme, werde ich über kotlin.reflect.KFunction
und java.lang.reflect.Constructor
/ java.lang.reflect.Method
schreiben.
Wie der Paketname andeutet, ist Ersteres eine Funktion in der Reflexion von "Kotlin".
In "Kotlin" können Methoden und Konstruktoren als dieselbe "KFunction" behandelt werden.
KFunction
kann das Standardargument durch Aufrufen von callBy
verwenden oder das Instanzargument in Method.invoke
von Java
unter bestimmten Bedingungen ignorieren.
Letzteres ist eine Funktion in der Java-Reflexion. Da "Konstruktor" und "Methode" als Typen getrennt sind, können sie nicht auf die gleiche Weise behandelt werden, und "Methode" erfordert Instanzparameter ("null" für "statische Methode" und Instanzen ansonsten).
Der Grund, warum ich zu Beginn schrieb: "Der Aufruf von" KFunction "ist langsamer als der Fall des direkten Aufrufs von" Constructor "/" Method "von" Java "" verwendet nicht das Standardargument (= alle Parameter sind ausgerichtet) " Dies ist ein Vergleich zwischen dem Aufruf von KFunction.callund dem Aufruf von
Constructor.newInstance /
Method.invoke`.
Es gibt ungefähr zwei Arten von Funktionsdefinitionsmethoden und vier Arten im Detail, von denen jede zusammengefasst ist.
--Konstrukteur
@ JvmStatic
)Der folgende Code wurde zur Überprüfung verwendet.
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("Für Konstrukteure")
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("Zum Beispiel Funktionen")
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("Für Methoden, die für Begleitobjekte definiert sind")
inner class CompanionObjectFunctionTest {
@Test
@DisplayName("Wenn durch Methodenreferenz erhalten")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::companionObjectFunctionSample
//Beim Abrufen mit Methodenreferenz sind keine Instanzparameter erforderlich
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Wenn durch Reflexion erworben")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "companionObjectFunctionSample" }
.let { it as KFunction<ConstructorSample> }
//Instanzparameter erforderlich, wenn durch Reflexion erfasst
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("Im Fall der im Begleitobjekt definierten Methode (mit angegebenem JvmStatic)")
inner class StaticMethodTest {
@Test
@DisplayName("Wenn durch Methodenreferenz erhalten")
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("Wenn durch Reflexion von einem Begleitobjekt erhalten")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "staticMethodSample" }
.let { it as KFunction<ConstructorSample> }
//Instanzparameter erforderlich, wenn durch Reflexion erfasst
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
}
Wenn die Quelle von KFunction
ein Konstruktor ist, können SieConstructor
mit KFunction.javaConstructor
abrufen.
Da der Konstruktor keine Instanzparameter usw. benötigt, können Sie "Constructor.newInstance" auf die gleiche Weise wie "KFunction.call" aufrufen.
@Suppress("UNCHECKED_CAST")
class CallJavaReflectionTest {
data class ConstructorSample(val foo: Int, val bar: String)
val expected = ConstructorSample(1, "2")
@Test
@DisplayName("Für Konstrukteure")
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"))
}
}
Wenn die Quelle von "KFunction" nicht der Konstruktor ist, können Sie "Method" mit "KFunction.javaMethod" abrufen. Wie oben erwähnt, benötigen Sie einen Instanzparameter ("null" für "statische Methode", andernfalls eine Instanz), um "Methode" aufzurufen.
Mit Ausnahme des Konstruktors wird hier das Verfahren zum Erfassen einer Instanz von "KFunction" unabhängig von der Generierungsmethode nicht offenbart, selbst wenn der Instanzparameter in "KFunction.call" allein von "KFunction" weggelassen wird. Sie können Method.invoke
[^ instance_hosoku] nicht aufrufen.
Selbst wenn Sie "JvmStatic" hinzufügen, können Sie "Method.invoke" nicht mit dem Instanzparameter "null" aufrufen, da dies die Definition für "Companion Object" ist, die durch die Methode zum Erfassen als "KFunction" erfasst werden kann.
[^ instance_hosoku]: Solange der Instanzparameter in KFunction.call
weggelassen werden kann, wird davon ausgegangen, dass die Instanzinformationen auf irgendeine Weise intern beibehalten werden, aber in der Schnittstelle von KFunction
verfügbar gemacht werden. Da KFunctionImpl
, eine Implementierungsklasse von KFunction
, eine interne
Klasse ist, wird beurteilt, dass wenn Sie es weiter verfolgen, es zu einer echten schwarzen Magie wird, und hier schließen wir, dass dies nicht möglich ist.
Um "Methode" von "KFunction" aufzurufen, die von einem anderen als dem Konstruktor erhalten wurde, ist es daher erforderlich, eine Instanz separat vorzubereiten.
@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("Zum Beispiel Funktionen")
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("Für Methoden, die für Begleitobjekte definiert sind")
inner class CompanionObjectFunctionTest {
@Test
@DisplayName("Wenn durch Methodenreferenz erhalten")
fun byMethodReferenceTest() {
val function: KFunction<ConstructorSample> = (CallJavaReflectionTest)::companionObjectFunctionSample
//Beim Abrufen mit Methodenreferenz sind keine Instanzparameter erforderlich
assertEquals(expected, function.call(1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
@Test
@DisplayName("Wenn durch Reflexion erworben")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "companionObjectFunctionSample" }
.let { it as KFunction<ConstructorSample> }
//Instanzparameter erforderlich, wenn durch Reflexion erfasst
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]: Wie in diesem Artikel zusammengefasst, können die in "Kotlin" definierten Funktionen nicht durch "statische Funktionen" erhalten werden Es wird weggelassen.
@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("Im Fall der im Begleitobjekt definierten Methode (mit angegebenem JvmStatic)")
inner class StaticMethodTest {
@Test
@DisplayName("Wenn durch Methodenreferenz erhalten")
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("Wenn durch Reflexion von einem Begleitobjekt erhalten")
fun byReflectionTest() {
val function: KFunction<ConstructorSample> = CallJavaReflectionTest::class.companionObject!!
.functions.first { it.name == "staticMethodSample" }
.let { it as KFunction<ConstructorSample> }
//Instanzparameter erforderlich, wenn durch Reflexion erfasst
assertEquals(expected, function.call(CallJavaReflectionTest::class.companionObjectInstance, 1, "2"))
val javaMethod: Method = function.javaMethod!!
assertEquals(expected, javaMethod.invoke(CallJavaReflectionTest::class.companionObjectInstance!!, 1, "2"))
}
}
}
Um den Aufrufaufwand von "KFunction" zu vermeiden, haben wir in diesem Artikel das Muster zusammengefasst, um "Constructor" / "Method" von "Java" durch die Definition von "KFunction" zu erhalten und es direkt aufzurufen. Als Ergebnis der Überprüfung wurde der Schluss gezogen, dass im Fall eines Konstruktors "Constructor.newInstance" von einer einzelnen "KFunction" aufgerufen werden kann und in anderen Fällen "Method.invoke" nur aufgerufen werden kann, wenn die Instanz separat abgerufen werden kann. Ich tat.
Es scheint, dass es am einfachsten ist, KFunction.callBy
zu verwenden, wenn Sie nur ein Werkzeug erstellen möchten, das funktioniert, aber wenn Sie ein Werkzeug unabhängig von der Ausführungsgeschwindigkeit erstellen möchten, können Sie Constructor
/ Method
direkt aufrufen. Sie können es versuchen.
Ich hoffe dieser Artikel hat dir geholfen.
Recommended Posts