kotlinadvanced
Type Class Pattern with Generics
Implement type class patterns in Kotlin: ad-hoc polymorphism, generic serializers, and extension-based dispatch.
kotlinPress ⌘/Ctrl + Shift + C to copy
// Type class: Printable
interface Printable<T> {
fun T.prettyPrint(): String
}
// Type class: Summable
interface Summable<T> {
fun zero(): T
fun add(a: T, b: T): T
}
// Type class: JsonSerializer
interface JsonSerializer<T> {
fun serialize(value: T): String
fun deserialize(json: String): T
}
// Instances for Int
object IntSummable : Summable<Int> {
override fun zero() = 0
override fun add(a: Int, b: Int) = a + b
}
object DoubleSummable : Summable<Double> {
override fun zero() = 0.0
override fun add(a: Double, b: Double) = a + b
}
object StringSummable : Summable<String> {
override fun zero() = ""
override fun add(a: String, b: String) = a + b
}
// Generic function using type class
fun <T> sumAll(items: List<T>, summable: Summable<T>): T =
items.fold(summable.zero()) { acc, item -> summable.add(acc, item) }
// Printable instances
object IntPrintable : Printable<Int> {
override fun Int.prettyPrint() = "Int($this)"
}
data class User(val name: String, val age: Int, val email: String)
object UserPrintable : Printable<User> {
override fun User.prettyPrint() =
"User { name=\"$name\", age=$age, email=\"$email\" }"
}
// JsonSerializer instances
object UserJsonSerializer : JsonSerializer<User> {
override fun serialize(value: User): String =
"""{ "name": "${value.name}", "age": ${value.age}, "email": "${value.email}" }"""
override fun deserialize(json: String): User {
val nameMatch = Regex(""""name":\s*"([^"]+)"""").find(json)
val ageMatch = Regex(""""age":\s*(\d+)""").find(json)
val emailMatch = Regex(""""email":\s*"([^"]+)"""").find(json)
return User(
name = nameMatch?.groupValues?.get(1) ?: "",
age = ageMatch?.groupValues?.get(1)?.toIntOrNull() ?: 0,
email = emailMatch?.groupValues?.get(1) ?: ""
)
}
}
// Registry pattern
object TypeClassRegistry {
private val summables = mutableMapOf<Class<*>, Summable<*>>()
private val serializers = mutableMapOf<Class<*>, JsonSerializer<*>>()
init {
summables[Int::class.java] = IntSummable
summables[Double::class.java] = DoubleSummable
summables[String::class.java] = StringSummable
serializers[User::class.java] = UserJsonSerializer
}
@Suppress("UNCHECKED_CAST")
fun <T : Any> summable(klass: Class<T>): Summable<T>? =
summables[klass] as? Summable<T>
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> summable(): Summable<T>? =
summable(T::class.java)
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> serializer(): JsonSerializer<T>? =
serializers[T::class.java] as? JsonSerializer<T>
}
// Generic operations using registry
inline fun <reified T : Any> List<T>.genericSum(): T? {
val summable = TypeClassRegistry.summable<T>() ?: return null
return fold(summable.zero()) { acc, item -> summable.add(acc, item) }
}
inline fun <reified T : Any> T.toJson(): String? =
TypeClassRegistry.serializer<T>()?.serialize(this)
fun main() {
// Summable
println("--- Summable ---")
println("Int sum: ${sumAll(listOf(1, 2, 3, 4, 5), IntSummable)}")
println("Double sum: ${sumAll(listOf(1.5, 2.5, 3.0), DoubleSummable)}")
println("String sum: ${sumAll(listOf("Hello", " ", "World"), StringSummable)}")
// Using registry
println("\n--- Registry ---")
println("Generic int sum: ${listOf(10, 20, 30).genericSum()}")
println("Generic double sum: ${listOf(1.1, 2.2, 3.3).genericSum()}")
// Printable
println("\n--- Printable ---")
with(IntPrintable) { println(42.prettyPrint()) }
with(UserPrintable) {
println(User("Alice", 30, "alice@test.com").prettyPrint())
}
// Serializer
println("\n--- Serializer ---")
val user = User("Bob", 25, "bob@test.com")
val json = user.toJson()
println("JSON: $json")
if (json != null) {
val restored = UserJsonSerializer.deserialize(json)
println("Restored: $restored")
println("Equal: ${user == restored}")
}
}Use Cases
- Ad-hoc polymorphism without inheritance
- Generic serialization frameworks
- Extensible type-driven dispatch
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinadvanced
Inline Functions and Reified Generics
Use inline functions for zero-overhead abstractions: reified type parameters, crossinline, and noinline.
Best for: Type-safe parsing and casting utilities
#kotlin#inline
kotlinadvanced
Generics — Variance, Bounds, and Type Projections
Master Kotlin generics: in/out variance, upper bounds, type projections, and generic constraints.
Best for: Type-safe generic APIs and containers
#kotlin#generics
kotlinbeginner
Enum Classes — Advanced Patterns
Use Kotlin enum classes with properties, methods, interfaces, and companion utilities.
Best for: Type-safe constant sets with behavior
#kotlin#enum
kotlinadvanced
Custom Property Delegates
Create reusable property delegates: validation, logging, caching, and thread-safe lazy initialization.
Best for: Input validation on property assignment
#kotlin#delegates