Kotlin Academy · Oppitunti

Reflektioon perustuva validointi ja kuvaus

Rakentakaa yksinkertainen validaattori tai oliomuunnin annotaatioihin perustuvalla reflektiolla.

Oppitunti 4/413 vaihetta

Reflektioon perustuva validointi ja kuvaus on ilmainen Kotlin Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Kotlin Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mihin tätä mallia käytetään

Reflektioon perustuva validointi lukee annotaatiot luokan ominaisuuksista suorituksen aikana ja soveltaa validointisääntöjä automaattisesti — kenttiä ei tarvitse tarkistaa erikseen käsin. Sama tekniikka on ORM-muuntimien taustalla: ne muuntavat objekteja tietokantariveiksi ja päinvastoin.

Validate-annotaatio

Määritelkää ominaisuuksiin kohdistuva annotaatio, joka säilytetään suorituksen aikana:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class Validate(
    val minLength: Int = 0,
    val maxLength: Int = Int.MAX_VALUE,
    val pattern: String = ""
)

Data-luokan annotointi

Soveltakaa @Validate-annotaatiota data-luokan ominaisuuksiin. Validaattori tarkistaa nämä suorituksen aikana:

data class UserForm(
    @Validate(minLength = 2, maxLength = 50) val name: String,
    @Validate(pattern = "^[\\w.]+@[\\w]+\\.[a-z]{2,}$") val email: String,
    @Validate(minLength = 8) val password: String
)

Validaattorifunktio

Käyttäkää KClass.declaredMemberProperties-ominaisuutta ominaisuuksien läpikäyntiin, annotaatioiden etsimiseen ja virheiden keräämiseen:

fun validate(obj: Any): List<String> {
    val errors = mutableListOf<String>()
    for (prop in obj::class.declaredMemberProperties) {
        val ann = prop.findAnnotation<Validate>() ?: continue
        val value = (prop as KProperty1<Any, *>).get(obj)?.toString() ?: ""
        if (value.length < ann.minLength) errors += "${prop.name}: too short"
        if (value.length > ann.maxLength) errors += "${prop.name}: too long"
        if (ann.pattern.isNotEmpty() && !value.matches(Regex(ann.pattern)))
            errors += "${prop.name}: invalid format"
    }
    return errors
}

Validaattorin suorittaminen

Kutsukaa validate()-funktiota mille tahansa annotaatiolla merkitylle objektille. Funktio palauttaa virheviestien luettelon; tyhjä luettelo tarkoittaa, että objekti on kelvollinen:

val form = UserForm("A", "not-an-email", "short")
val errors = validate(form)
errors.forEach(::println)
// name: too short
// email: invalid format
// password: too short

Objektin muuntaminen Map-rakenteeksi @Column-annotaatiolla

Toinen reflektiomalli muuntaa objektin rakenteeksi Map käyttämällä @Column-annotaatiota sarakkeiden nimien ilmoittamiseen:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY)
annotation class Column(val name: String)

fun toMap(obj: Any): Map<String, Any?> =
    obj::class.declaredMemberProperties
        .mapNotNull { prop ->
            val col = prop.findAnnotation<Column>() ?: return@mapNotNull null
            col.name to (prop as KProperty1<Any, *>).get(obj)
        }.toMap()

Muuntimen käyttäminen

Annotoikaa data-luokka ja kutsukaa toMap()-funktiota saadaksenne sarakkeiden nimillä avaimet sisältävän kartan, joka on valmis INSERT-lauseketta varten:

data class User(
    @Column("user_name") val name: String,
    @Column("user_email") val email: String
)

val user = User("Alice", "alice@example.com")
println(toMap(user))
// {user_name=Alice, user_email=alice@example.com}

Reflektion tulosten välimuistitus

Reflektioskannaukset ovat raskaita. Välimuistittakaa ominaisuuksien ja annotaatioiden välinen kuvaus käynnistyksen yhteydessä käyttämällä ConcurrentHashMap-rakennetta, jonka avaimina ovat KClass-arvot:

val cache = ConcurrentHashMap<KClass<*>, List<Pair<KProperty1<Any,*>, Column>>>()

fun columnsOf(klass: KClass<*>) = cache.getOrPut(klass) {
    klass.declaredMemberProperties
        .mapNotNull { p ->
            p.findAnnotation<Column>()?.let { (p as KProperty1<Any,*>) to it }
        }
}

Validoinnin ja muuntamisen yhdistäminen

Voitte pinota useita annotaatioita yhdelle ominaisuudelle ja käsitellä kutakin itsenäisesti. Validoikaa ensin ja muuntakaa sen jälkeen — tai tehkää molemmat yhdellä läpikäynnillä keräämällä kunkin annotaatiotyypin tiedot:

data class Product(
    @Validate(minLength = 1) @Column("product_name") val name: String,
    @Column("price") val price: Double
)

Rajoitukset

Reflektioon perustuva validointi on tehokasta, mutta siihen liittyy kompromisseja: se lisää suorituksenaikaista kuormitusta, riippuu annotaatioiden säilytyskäytännöstä eikä pysty havaitsemaan virheitä käännösaikana. Jos suorituskyky tai käännösaikainen turvallisuus on erityisen tärkeää, harkitkaa sen sijaan KSP:n avulla luotua koodia.

Milloin tätä mallia kannattaa käyttää

Reflektioon perustuva validointi on parhaimmillaan kehyskoodissa, jonka on käsiteltävä mielivaltaisia käyttäjän määrittämiä luokkia: ORM-järjestelmissä, serialisoijissa, lomakevalidaattoreissa ja asetusten sitojissa. Sovelluskoodissa, joka hallitsee omat tyyppinsä, kannattaa nopeuden ja selkeyden vuoksi suosia eksplisiittistä validointia.

Pikatarkistus

Mikä annotaation säilytyskäytäntö vaaditaan, jotta validointiannotaatio voidaan lukea suorituksen aikana?

Kertaus: reflektioon perustuva validointi ja muuntaminen

Tärkeimmät opit:

  • Määritelkää ominaisuuksiin kohdistuvat annotaatiot, jotka säilytetään suorituksen aikana
  • Käyttäkää declaredMemberProperties- ja findAnnotation-toimintoja validoinnin tai muuntamisen ohjaamiseen
  • Välimuistittakaa tulokset KClass-arvojen perusteella, jotta toistuvan reflektion aiheuttama kuormitus vältetään
  • Pinotkaa ominaisuudelle useita annotaatioita yhdistetyn toiminnan saavuttamiseksi
  • Harkitkaa KSP:tä käännösaikaisena vaihtoehtona suorituskykyä vaativissa kohdissa
Aloita maksutta

Opi Kotlin tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
51
Oppitunnit
203

Usein kysytyt kysymykset

Onko oppitunti ”Reflektioon perustuva validointi ja kuvaus” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Kotlin Academy-oppimispolun 3 oppituntia, myös oppitunnin “Reflektioon perustuva validointi ja kuvaus”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Reflektioon perustuva validointi ja kuvaus”?

Rakentakaa yksinkertainen validaattori tai oliomuunnin annotaatioihin perustuvalla reflektiolla. Harjoittelet Kotlin Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Kotlin Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Kotlin Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Reflektioon perustuva validointi ja kuvaus”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Kotlin Academy-oppitunnilla?

Kyllä. Jokainen Kotlin Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Kotlin-reflektio: KClass, KFunction, KProperty
  2. Mukautettujen annotaatioiden luominen ja kohdentaminen
  3. Annotaatioiden lukeminen ajon aikana
  4. Reflektioon perustuva validointi ja kuvaus
← Takaisin: Kotlin Academy