Валидация и отображение на основе рефлексии
Создайте простой валидатор или преобразователь объектов с помощью рефлексии на основе аннотаций.
«Валидация и отображение на основе рефлексии» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Рефлексия Kotlin: KClass, KFunction, KProperty
- Создание и применение пользовательских аннотаций
- Чтение аннотаций во время выполнения
- Валидация и отображение на основе рефлексии