A type-safe API hides unavoidable unchecked logic behind a controlled interface, so callers cannot combine a type with the wrong validator or operation. section 11.3.6
Typed validator registry
A private map stores validators for different classes, while generic registerValidator and get functions connect each KClass<T> to FieldValidator<T>. The implementation contains one unchecked cast; callers receive the correct validator, and an incorrect call such as Validators[String::class].validate(42) fails at compile time. section 11.3.6
The pattern separates safe usage from unsafe storage:
- A heterogeneous map uses KClass<*> and FieldValidator<*> because the stored generic arguments are unknown. section 11.3.6
- Direct retrieval prevents input because FieldValidator<*> cannot safely accept a value of an unknown type. section 11.3.6
- Encapsulation localizes the unchecked cast and prevents callers from corrupting the collection. section 11.3.6
Listing 11.21 Encapsulating access to the validator collection
object Validators {
private val validators = #1
mutableMapOf<KClass<*>, FieldValidator<*>>()
fun <T: Any> registerValidator(
kClass: KClass<T>, fieldValidator: FieldValidator<T>) {
validators[kClass] = fieldValidator #2
}
@Suppress("UNCHECKED_CAST") #3
operator fun <T: Any> get(kClass: KClass<T>): FieldValidator<T> =
validators[kClass] as? FieldValidator<T>
?: throw IllegalArgumentException(
"No validator for ${kClass.simpleName}")
}
fun main() {
Validators.registerValidator(String::class, DefaultStringValidator)
Validators.registerValidator(Int::class, DefaultIntValidator)
println(Validators[String::class].validate("Kotlin"))
// true
println(Validators[Int::class].validate(42))
// true
}
Inspect the complete Kotlin registry implementation, including registration, lookup, encapsulation, and the localized unchecked cast. section 11.3.6
