0Pricing
Kotlin Academy · Урок

Валидация и отображение на основе рефлексии

Создайте простой валидатор или преобразователь объектов с помощью рефлексии на основе аннотаций.

«Валидация и отображение на основе рефлексии» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Какую задачу решает этот шаблон

Проверка на основе рефлексии во время выполнения читает аннотации свойств класса и автоматически применяет правила проверки — без ручных проверок каждого поля. Та же техника лежит в основе сопоставителей ORM, преобразующих объекты в строки базы данных и обратно.

Аннотация Validate

Определите аннотацию, сохраняемую во время выполнения и применяемую к свойствам:

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

Аннотирование класса данных

Примените @Validate к свойствам класса данных. Средство проверки просканирует их во время выполнения:

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
)

Функция проверки

Используйте KClass.declaredMemberProperties, чтобы перебрать свойства, найти аннотации и собрать ошибки:

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
}

Запуск проверки

Вызовите validate() для любого аннотированного объекта. Функция возвращает список сообщений об ошибках; пустой список означает, что объект корректен:

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

Преобразование объекта в карту с помощью @Column

Другой шаблон рефлексии преобразует объект в Map, используя аннотацию @Column для задания имен столбцов:

@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()

Использование преобразователя

Аннотируйте класс данных и вызовите toMap(), чтобы получить карту с ключами-столбцами, готовую для оператора INSERT:

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}

Кэширование результатов рефлексии

Сканирование с помощью рефлексии требует значительных ресурсов. Кэшируйте соответствия свойств и аннотаций при запуске, используя ConcurrentHashMap с ключами типа KClass:

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

Объединение проверки и преобразования

Вы можете объединить несколько аннотаций в одном свойстве и обрабатывать каждую независимо. Сначала выполните проверку, затем преобразование — или сделайте и то и другое за один проход, собирая аннотации каждого типа:

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

Ограничения

Проверка на основе рефлексии эффективна, но имеет свои компромиссы: она увеличивает накладные расходы во время выполнения, зависит от сохранения аннотаций и не может обнаруживать ошибки во время компиляции. Для проверки, критичной к производительности или безопасной на этапе компиляции, рассмотрите вместо этого код, сгенерированный KSP.

Когда использовать этот шаблон

Проверка на основе рефлексии особенно полезна в коде фреймворков, который должен работать с произвольными классами, определенными пользователем: в ORM, сериализаторах, средствах проверки форм и привязки конфигурации. В коде приложения, где типы заранее известны, следует предпочесть явную проверку ради скорости и ясности.

Быстрая проверка

Какая политика сохранения аннотации требуется, чтобы аннотация проверки была доступна во время выполнения?

Итоги: проверка и преобразование на основе рефлексии

Основные выводы:

  • Объявляйте аннотации, сохраняемые во время выполнения и применяемые к свойствам
  • Используйте declaredMemberProperties и findAnnotation для выполнения проверки или преобразования
  • Кэшируйте результаты для каждого KClass, чтобы избежать повторных затрат на рефлексию
  • Объединяйте несколько аннотаций в одном свойстве для совместного поведения
  • Рассмотрите KSP как альтернативу во время компиляции на участках, критичных к производительности

Часто задаваемые вопросы

Урок «Валидация и отображение на основе рефлексии» бесплатный?

Да — полный текст урока «Валидация и отображение на основе рефлексии» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Валидация и отображение на основе рефлексии»?

Создайте простой валидатор или преобразователь объектов с помощью рефлексии на основе аннотаций. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Валидация и отображение на основе рефлексии»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Рефлексия Kotlin: KClass, KFunction, KProperty
  2. Создание и применение пользовательских аннотаций
  3. Чтение аннотаций во время выполнения
  4. Валидация и отображение на основе рефлексии
← Назад к Kotlin Academy