[Kotlin] Holen Sie sich Java Constructor / Method von KFunction und rufen Sie es auf

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".

Vorwort

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 vonConstructor.newInstance / Method.invoke`.

Text

Es gibt ungefähr zwei Arten von Funktionsdefinitionsmethoden und vier Arten im Detail, von denen jede zusammengefasst ist.

--Konstrukteur

Der folgende Code wurde zur Überprüfung verwendet.

Gesamtprogramm zur Überprüfung
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"))
        }
    }
}

Für Konstrukteure

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"))
    }
}

Anders als der Konstruktor

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.

Zum Beispiel Funktionen

@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"))
    }
}

Für Funktionen, die für Begleitobjekte definiert sind

@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"))
        }
    }
}

Für Begleitobjekte (mit @ 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"))
        }
    }
}

Zusammenfassung

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

[Kotlin] Holen Sie sich Java Constructor / Method von KFunction und rufen Sie es auf
[Java] KFunction von Method / Constructor in Java abrufen [Kotlin]
Rufen Sie eine Methode mit Kotlins Rückrufblock von Java aus auf
Schreiben Sie eine Klasse in Kotlin und nennen Sie sie in Java
Implementieren Sie die Java-Schnittstelle in der JRuby-Klasse und rufen Sie sie von Java aus auf
Rufen Sie Java-Methoden aus JavaScript auf, das in Java ausgeführt wird
[Java] Json von der URL mit der Standard-API (javax.script) abrufen und verarbeiten
[Android] Rufen Sie Kotlins Standardargumentmethode von Java aus auf
Java-Methodenaufruf von RPG (Methodenaufruf in eigener Klasse)
Rufen Sie Java von JRuby aus auf
JAVA-Konstruktoraufrufverarbeitung
Java-Sprache aus der Sicht von Kotlin und C #
[Java] [Kotlin] Rufen Sie valueOf und Werte von Enum generisch auf
[Android-Entwicklung] Holen Sie sich mit Java Bilder vom Server und legen Sie sie in ImageView fest! !!
[Java] Mit Apathce Tika Metadaten aus Dateien abrufen und Breite und Höhe von Bildern / Videos aus Metadaten abrufen [Kotlin]
Memorandum Nr. 4 "Holen Sie sich eine Zeichenkette und dekorieren Sie sie" [Java]
[Java] Zeichensatz mit Apathce Tika abrufen / String von Zeichensatz initialisieren [Kotlin]
Holen Sie sich Videoinformationen von Nikorepo und werfen Sie sie zu Slack
Java-Methoden und Methodenüberladungen
Ruft Attribute und Werte aus einer XML-Datei in Java ab
Suchen und Ausführen einer Methode aus einer Instanz mit Verarbeitung (Java)
Installieren Sie das memcached Plugin unter MySQL und greifen Sie von Java aus zu
Hinweise zum Erstellen der Kotlin-Entwicklungsumgebung und zur Migration von Java nach Kotlin
[Java] So konvertieren Sie vom Typ String in den Pfadtyp und erhalten den Pfad
Rufen Sie Kotlins versiegelte Klasse von Java aus an
Unterschiede zwischen "Anfänger" Java und Kotlin
Land von IP-Adresse abrufen (Java)
[Java-Anfänger] == Operator und Gleiche Methode
Java, Überlastungskonstruktor ab Anfänger
Rufen Sie die Java-API von TensorFlow von Scala aus auf
Rufen Sie die Super-Methode in Java auf
[Java] Übergeben Sie Argumente an den Konstruktor in Mockito / Set default call to method an callRealMethod
JSON in Java und Jackson Teil ③ Betten Sie JSON in HTML ein und verwenden Sie es aus JavaScript
Gradle generiert automatisch eine Versionsnummer aus git und verwendet diese in Java
Wählen Sie die erste nicht leere aus mehreren Optional aus und rufen Sie ihre Methode auf
[Java] Holen Sie sich MimeType aus dem Inhalt der Datei mit Apathce Tika [Kotlin]
Konvertierung zwischen Kotlin nullable und Java Optional
Java für Anfänger, Variablen und Typen
Wie man die Java Silver Prüfung ablegt und wie man lernt
[Kotlin] Drei Möglichkeiten, um Klasse von KClass zu bekommen
Rufen Sie die Java-Bibliothek von C mit JNI auf
Abrufen von Anruferinformationen aus dem Stack-Trace (Java)
[Für Anfänger] Unterschied zwischen Java und Kotlin
[Java] Tag-Informationen aus Musikdateien abrufen
Ein Java-Ingenieur verglich Swift, Kotlin und Java.
[Java] Vergleich von Sammlungs- und StringBuilder-Operationsmethoden
Memo zur Überwachungsmethode für Verzeichnisänderungen (Java, Kotlin)
Abrufen des Verlaufs vom Zabbix-Server in Java
[Java] Instanzmethode, Instanzfeld, Klassenmethode, Klassenfeld, Konstruktorzusammenfassung
[Java8] Durchsuchen Sie das Verzeichnis und holen Sie sich die Datei
Memo für die Migration von Java nach Kotlin
[Java] TreeMap-Nachfolgemethode und Komplexität von entrySet
[Java] Abrufen und Anzeigen des Datums 10 Tage später mithilfe der von Java 8 hinzugefügten Zeit-API.
[Java] Beispiel eines Programms, das die Maximal- und Minimalwerte von einem Array abruft