Kotlin Generic Type Declarations: Common Patterns and Examples

Generics <T> allow you to write reusable, type-safe code without committing to a specific type upfront.
They’re commonly used in collections, classes, functions, interfaces, and function types throughout Kotlin.
This article walks through some of the most common generic type declaration patterns you’ll encounter in real-world Kotlin code.
List
Specify the type of elements stored in a list.
val names: List<String> = listOf("Alice", "Bob")
Only String values can be stored in this list.
Map<K, V>
Specify the types of keys and values.
val users: Map<Int, String> = mapOf(1 to "Alice")
K stands for key and V stands for value.
MutableList
A mutable list whose contents can be changed.
val names: MutableList<String> = mutableListOf()
Elements can be added, removed, or updated.
Nested Generics
Generics can be combined to represent more complex structures.
val cache: Map<String, List<Int>> = mapOf(
"scores" to listOf(1, 2, 3)
)
This pattern is common when working with APIs and data models.
Generic Classes
A generic class can work with different types.
class Box<T>(val value: T)
val box = Box<String>("Hello")
The same class can be reused with any type.
Multiple Type Parameters
A class can define more than one type parameter.
class PairBox<K, V>(val key: K, val value: V)
val user = PairBox<Int, String>(1, "Alice")
This pattern is similar to how Map<K, V> works.
Generic Functions
The type is determined when the function is called.
fun <T> first(items: List<T>): T? = items.firstOrNull()
val result = first(listOf("A", "B"))
Generic functions make shared logic reusable across different types.
Generic Extension Functions
Extension functions can also be generic.
fun <T> List<T>.second(): T? = getOrNull(1)
This works with any List<T>.
Generic Interfaces
The implementation decides which type will be used.
interface Repository<T> {
fun save(item: T)
}
class UserRepository : Repository<String>
This is a common pattern in repository and service APIs.
Function Types
Function types can also include generic types.
val formatter: (Int) -> String = { "Number: $it" }
This function takes an Int and returns a String.
Type Aliases
Give complex generic types a more readable name.
typealias Callback<T> = (T) -> Unit
val callback: Callback<String> = {
println(it)
}
This improves readability and reduces repetition.
Generic Functional Interfaces
Useful when working with SAM conversion.
fun interface Transformer<T, R> {
fun transform(value: T): R
}
val transformer: Transformer<Int, String> = Transformer {
"Number: $it"
}
This allows lambdas to be used with strongly typed interfaces.
Nullable Generics
Allow nullable values inside generic types.
val names: List<String?> = listOf("Alice", null)
Notice the nullable type String?.
Star Projections
Use * when the generic type is unknown.
val items: List<*> = listOf("A", 1, true)
This is Kotlin’s equivalent of Java’s List<?>.
Covariance (out)
Used for producer types that only provide values.
val numbers: List<out Number>
Allows subtype values to be treated safely as a parent type.
Contravariance (in)
Used for consumer types that receive values.
val consumer: Comparable<in String>
Often appears in framework and library APIs.
Reified Type Parameters
Keep type information available at runtime.
inline fun <reified T> isType(value: Any): Boolean = value is T
isType<String>("Hello")
Frequently used in serialization and JSON libraries.
Type Constraints
Restrict which types can be used.
fun <T : Number> double(value: T): Double = value.toDouble() * 2
Only types that extend Number are allowed.
Multiple Constraints
Apply more than one requirement to a type parameter.
fun <T> process(value: T) where T : CharSequence, T : Comparable<T> {
}
Useful for advanced reusable APIs.
Summary
List<String> // Collection element type
Map<K, V> // Key and value types
Box<T> // Generic class
fun <T> // Generic function
Repository<T> // Generic interface
(T) -> R // Function type
Callback<T> // Type alias
List<*> // Unknown type
out T // Covariance
in T // Contravariance
reified T // Runtime type information
<T : Number> // Type constraint
In day-to-day Android and Kotlin development, you’ll most often encounter List<T>, Map<K, V>, fun <T>, and function types such as (T) -> R. More advanced features like out, in, reified, and type constraints tend to appear when building reusable libraries and frameworks.