Refleksion med Kotlin

1. Introduktion

Refleksion er navnet på evnen til at inspicere, indlæse og interagere med klasser, felter og metoder under kørsel. Vi kan gøre dette, selv når vi ikke ved, hvad de er på kompileringstidspunktet.

Dette har et stort antal anvendelser, afhængigt af hvad vi udvikler. For eksempel gør rammer som Spring kraftigt brug af det.

Support til dette er indbygget i JVM og er derfor implicit tilgængelig for alle JVM-baserede sprog. Nogle JVM-sprog har dog ekstra support ud over det, der allerede er tilgængeligt.

2. Java-refleksion

Alle standard Java Reflection-konstruktioner er tilgængelige og fungerer perfekt sammen med vores Kotlin-kode. Dette inkluderer java.lang.Klasse klasse såvel som alt i java.lang.reflekteret pakke.

Hvis vi vil bruge standard Java Reflection API'er af en eller anden grund, kan vi gøre det på nøjagtig samme måde som i Java. For eksempel for at få en liste over alle de offentlige metoder i en Kotlin-klasse ville vi gøre:

MyClass :: class.java.methods

Dette opdeles i følgende konstruktioner:

  • MyClass :: klasse giver os Kotlin klasse repræsentation for Min klasse klasse
  • .java giver os java.lang.Klasse tilsvarende
  • .metoder er et opkald til java.lang.Class.getMethods () tilgangsmetode

Dette fungerer nøjagtigt det samme, hvad enten det kaldes fra Java eller Kotlin, og om det kaldes på en Java- eller en Kotlin-klasse. Dette inkluderer Kotlin-specifikke konstruktioner, såsom dataklasser.

dataklasse ExampleDataClass (val navn: String, var aktiveret: Boolean) ExampleDataClass :: class.java.methods.forEach (:: println)

Kotlin konverterer også de returnerede typer til Kotlin-repræsentationerne.

I ovenstående får vi en kotlin. matrix som vi kan ringe til for hver().

3. Forbedringer af Kotlin-refleksion

Mens vi kan bruge standard Java Reflection API'er, er det ikke opmærksom på alle udvidelser, som Kotlin bringer til platformen.

Derudover kan det til tider være lidt akavet at bruge i nogle situationer. Kotlin bringer sin egen refleksions-API, som vi kan bruge til at løse disse problemer.

Alle indgangspunkter i Kotlin Reflection API bruger referencer. Tidligere så vi brugen af :: klasse for at give en henvisning til klassedefinitionen. Vi kan også bruge dette til at få henvisninger til metoder og egenskaber.

3.1. Kotlin-klassehenvisninger

Kotlin Reflection API giver adgang til en klassehenvisning. Dette kan derefter bruges til at introspektere de fulde detaljer om Kotlin-klassen. Dette giver adgang til Java-klassereferencen - java.lang.Klasse objekt - men også til alle de Kotlin-specifikke detaljer.

Kotlin API til klassedetaljer centrerer omkring kotlin.reflect.KClass klasse. Dette kan tilgås ved hjælp af :: operatør fra ethvert klassenavn eller forekomst - f.eks. String :: klasse.

Alternativt kan du få adgang til den ved hjælp af udvidelsesmetoden java.lang.Class.kotlin hvis en Java Klasse forekomst er tilgængelig for os:

val listClass: KClass = List :: class val name = "Baeldung" val stringClass: KClass = name :: class val someClass: Class val kotlinClass: KClass = someClass.kotlin

Når vi har opnået en KClass objekt, der er nogle enkle ting, som det kan fortælle os om den pågældende klasse. Nogle af disse er standard Java-koncepter, og andre er Kotlin-specifikke koncepter.

For eksempel kan vi nemt finde ud af, om en klasse er abstrakt eller endelig, men vi kan også finde ud af, om klassen er en dataklasse eller en ledsagerklasse:

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

Vi har også måder at bevæge os rundt i klassehierarkiet. I Java kan vi allerede flytte fra en klasse til dens superklasse, grænseflader og den ydre klasse, den er lukket i - hvis det er relevant.

Kotlin tilføjer til dette evnen til at opnå Companion Object for en vilkårlig klasse, og Objekt eksempel for en objektklasse:

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

Vi kan også oprette nye forekomster af en klasse fra en klassereference, på samme måde som i Java:

val listClass = ArrayList :: class val list = listClass.createInstance () assertTrue (listen er ArrayList)

Alternativt kan vi få adgang til konstruktørerne og bruge en eksplicit, hvis vi har brug for det. Disse er alle Metodehenvisninger som beskrevet i det næste afsnit.

På en meget lignende måde kan vi få adgang til alle metoderne, egenskaberne, udvidelserne og andre medlemmer af klassen:

val bigDecimalClass = BigDecimal :: klasse println (bigDecimalClass.constructors) println (bigDecimalClass.functions) println (bigDecimalClass.memberProperties) println (bigDecimalClass.memberExtensionFunctions)

3.2. Kotlin-metodehenvisninger

Ud over at være i stand til at interagere med klasser, vi kan også interagere med metoder og egenskaber.

Dette inkluderer klasseegenskaber - defineret med val eller var, standard klassemetoder og topniveaufunktioner. Som før fungerer dette lige så godt på kode skrevet i standard Java som på kode skrevet i Kotlin.

På nøjagtig samme måde som med klasser, vi kan få en henvisning til en metode eller ejendom ved hjælp af:: operatør.

Dette ser nøjagtigt ud som i Java 8 for at opnå en metodereference, og vi kan bruge det på nøjagtig samme måde. I Kotlin kan denne metodehenvisning også bruges til at få refleksionsinformation om målet.

Når vi først har fået en metodereference, kan vi kalde det som om det virkelig var den pågældende metode. Dette er kendt som en kaldbar reference:

val str = "Hello" val lengthMethod = str :: length assertEquals (5, lengthMethod ())

Vi kan også få flere detaljer om selve metoden på samme måde, som vi kan for klasser. Dette inkluderer både standard Java-detaljer samt Kotlin-specifikke detaljer, f.eks. Hvis metoden er en operatør eller hvis det er inline:

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

Ud over dette kan vi få mere information om metodens input og output gennem denne reference.

Dette inkluderer detaljer om returtypen og parametrene, herunder Kotlin-specifikke detaljer - såsom nullitet og optionalitet.

val str = "Hej" val metode = 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 ejendomsreferencer

Dette fungerer nøjagtigt det samme for egenskaber også, selvom de åbenlyse detaljer kan opnås er forskellige. Egenskaber kan i stedet informere os, hvis de er konstanter, sent initialiserede eller ændrede:

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

Bemærk, at egenskabskonceptet også fungerer i enhver ikke-Kotlin-kode. Disse identificeres af felter, der følger JavaBeans-konventionerne vedrørende getter- og settermetoder.

Dette inkluderer klasser i Java-standardbiblioteket. F.eks Kan kastes klasse har en ejendom Kan kastes. Besked i kraft af, at der er en metode getMessage () defineret i det.

Vi kan få adgang til den faktiske ejendom gennem metodereferencer, der er eksponeret - det getter og setter metoder. Det setter er kun tilgængelig, hvis vi arbejder med en KMutableProperty - dvs. ejendommen blev erklæret som var, hvorimod getter er altid tilgængelig.

Disse udsættes på en lettere at bruge måde via få() og sæt() metoder. Det getter og setter værdier er faktiske metodereferencer, der giver os mulighed for at arbejde med dem nøjagtigt det samme som enhver anden metodehenvisning:

val prop = dette :: mutableProperty assertEquals (String :: class.starProjectedType, prop.getter.returnType) prop.set ("Hello") assertEquals ("Hello", prop.get ()) prop.setter ("World") assertEquals ("Verden", prop.getter ())

4. Resume

Denne artikel giver et overblik over nogle af de ting, der kan opnås med refleksion i Kotlin, herunder både hvordan det interagerer med og adskiller sig fra reflektionsfunktionerne indbygget i standard Java-sproget.

Alle eksemplerne er tilgængelige på GitHub.