Question
I'm trying to understand the purpose of the reified keyword in Kotlin. It seems to allow reflection-like operations on generic types.
However, when I remove it, my code still appears to work fine. In what situations does reified actually make a real difference?
Short Answer
By the end of this page, you will understand what reified means in Kotlin, why it only works with inline functions, and when it is necessary instead of a normal generic type parameter. You will also see the difference between generic type information that is erased at runtime and type information that becomes available through reification.
Concept
In Kotlin, generic type parameters such as T are usually erased at runtime. This is called type erasure.
That means inside a normal generic function, the program often does not know the actual type that was used for T once the code is running.
For example, in a regular function like this:
fun <T> printType(value: T) {
// You cannot reliably use T::class here
}
T is mainly useful at compile time. The compiler checks types for safety, but the runtime usually does not keep that generic type information.
reified changes this behavior for inline functions.
When you write:
inline fun <reified T> example() {
println(T::class)
}
Kotlin replaces the function call with the actual function body at compile time. Because of that, the compiler can substitute the real type argument directly into the generated code.
So instead of treating T as an unknown erased type, the function can act as if the actual type is available at runtime.
This matters when you want to do things like:
- access
T::class - check
value is T - cast with
value as? T - call APIs that need a
ClassorKClassderived fromT
Without reified, you usually have to pass the type explicitly:
fun <T : Any> example(type: kotlin.reflect.KClass<T>) {
println(type)
}
example(String::class)
With reified, Kotlin can often infer and provide that type for you:
inline fun <reified T : Any> example() {
println(T::class)
}
example<String>()
Why this matters in real programming:
- It makes APIs cleaner.
- It reduces boilerplate like passing
MyType::classmanually. - It is very common in utility functions for JSON parsing, dependency lookup, filtering, and type-safe helpers.
Important limitation:
reifiedonly works on type parameters of inline functions.- It does not magically preserve all generic information everywhere.
- It helps because the compiler inserts the concrete type at each call site.
Mental Model
Think of a normal generic type T as a label written in pencil on a box.
At compile time, the label helps you organize things correctly. But at runtime, the label gets erased, so the program no longer clearly sees what was written.
A reified type is like turning that pencil label into a printed sticker right when the box is handed over.
Because the function is inline, Kotlin copies the function body directly to the call site and can replace T with the real type, such as String or Int.
So:
- normal generic
T= the runtime often forgets the exact type reified T= the actual type is written into the generated code at the call site
That is why reified is useful when the code needs to inspect or check the type while running.
Syntax and Examples
Basic syntax
reified must be used with an inline function:
inline fun <reified T> showType() {
println(T::class)
}
Call it like this:
showType<String>()
showType<Int>()
Example: checking a type
inline fun <reified T> isOfType(value: Any): Boolean {
return value is T
}
fun main() {
println(isOfType<String>("hello")) // true
println(isOfType<String>(123)) // false
}
Why this is useful
Without reified, this does not work:
fun <T> isOfType(value: Any): Boolean {
// return value is T
return false
}
The compiler rejects it because T is erased.
Example: getting a class reference
inline fun <reified T : Any> printClassName() {
println(T::class.simpleName)
}
fun main() {
printClassName<String>() // String
printClassName<List<Int>>() // List
}
Equivalent non-reified approach
If you do not use reified, you often pass the class explicitly:
fun <T : Any> printClassName(type: kotlin.reflect.KClass<T>) {
println(type.simpleName)
}
fun main() {
printClassName(String::class)
}
This version still works, but it is more verbose.
When leaving out reified still works
If your generic function never needs runtime access to the type, reified is unnecessary.
For example:
fun <T> firstItem(items: List<T>): T? {
return items.firstOrNull()
}
This works perfectly without reified because the function only moves values around using compile-time type safety. It does not inspect T at runtime.
Step by Step Execution
Consider this code:
inline fun <reified T> matchesType(value: Any): Boolean {
return value is T
}
fun main() {
val result1 = matchesType<String>("Kotlin")
val result2 = matchesType<String>(42)
println(result1)
println(result2)
}
What happens step by step
- The compiler sees that
matchesTypeis markedinline. - Instead of generating one shared generic function call, Kotlin copies the function body into each call site.
- For
matchesType<String>("Kotlin"),Tis replaced withString. - The generated logic becomes effectively similar to:
val result1 = "Kotlin" is String
- For
matchesType<String>(42), the generated logic becomes effectively similar to:
val result2 = 42 is String
- The first expression is
true. - The second expression is
false. - The program prints:
true
false
Compare that with a normal generic function
If the function were not inline and not reified, Kotlin would only know there is some T, but not which concrete type to check at runtime.
That is the key difference: reified turns the type argument into something usable in the generated runtime code.
Real World Use Cases
reified is most useful when a helper function needs to know the concrete type at runtime.
1. JSON parsing helpers
Libraries often need a target type.
inline fun <reified T> parseJson(json: String): T {
// Example idea: a JSON library uses T's type here
throw NotImplementedError("Demo only")
}
This is cleaner than forcing callers to pass T::class every time.
2. Dependency injection or service lookup
inline fun <reified T> resolve(): T {
throw NotImplementedError("Demo only")
}
A container can look up an object by type.
3. Filtering collections by type
Kotlin standard library uses this idea:
val mixed: List<Any> = listOf("a", 1, "b", 2)
val strings = mixed.filterIsInstance<String>()
println(strings) // [a, b]
This works because the function needs runtime type information.
4. Android and framework utilities
Developers use reified helpers for:
- creating intents
- retrieving fragments
- reading bundles
- locating serializers
- registering handlers by type
5. Safer casting helpers
inline fun <reified T> castOrNull(value: Any): T? {
return value as? T
}
This is convenient when converting loosely typed data into strongly typed values.
Real Codebase Usage
In real Kotlin projects, reified usually appears in small utility functions that make APIs easier to call.
Common patterns
Guard clauses based on type
inline fun <reified T> requireType(value: Any) {
require(value is T) { "Expected ${T::class.simpleName}" }
}
This is useful for validation and defensive programming.
Wrapper functions around libraries
A library may require a Class<T> or KClass<T>. Teams often write a reified wrapper:
fun <T : Any> loadConfig(type: kotlin.reflect.KClass<T>): T {
throw NotImplementedError("Demo only")
}
inline fun <reified T : Any> loadConfig(): T {
return loadConfig(T::class)
}
This keeps the core implementation explicit while giving callers a cleaner API.
Type-safe lookups
class Container {
private val services = mutableMapOf<Class<*>, Any>()
fun <T : Any> register(type: Class<T>, instance: T) {
services[type] = instance
}
inline fun <reified T : Any> get(): T? {
return services[T::class.java] as? T
}
}
Filtering mixed collections
val events: List<Any> = listOf("start", 1, true, "stop")
val textEvents = events.filterIsInstance<String>()
Why teams use it carefully
Although reified is convenient, developers do not use it everywhere.
They usually prefer it when:
- runtime type checks are actually needed
- a wrapper around reflection or class tokens improves readability
- the function is small enough to be safely inlined
They avoid it when:
- the function does not need runtime type information
- inlining would duplicate too much code
- passing a
ClassorKClassexplicitly is clearer
Common Mistakes
1. Thinking reified is needed for all generics
This is the most common misunderstanding.
Broken idea:
inline fun <reified T> identity(value: T): T {
return value
}
This works, but reified adds no benefit here because the function never uses T at runtime.
Use reified only when you need runtime access to the type.
2. Forgetting that reified requires inline
Broken code:
fun <reified T> printType() {
println(T::class)
}
This will not compile.
Correct version:
inline fun <reified T> printType() {
println(T::class)
}
3. Assuming reified preserves all nested generic details
Example:
inline fun <reified T> show() {
println(T::class)
}
show<List<String>>()
You may see List as the runtime class, not all nested generic details in the way beginners often expect.
reified helps with the top-level type argument used in the inline function, but generic type handling still has limits.
4. Confusing compile-time type safety with runtime type information
This works without reified:
fun <T> echo(value: T): T = value
This does not need runtime inspection.
This needs reified:
inline fun <reified T> isType(value: Any) = value is T
Ask yourself: Do I need to inspect the type while the program is running?
If not, reified is probably unnecessary.
5. Overusing reified wrappers
Sometimes a direct parameter is simpler and clearer:
fun <T : Any> create(type: kotlin.reflect.KClass<T>): T {
throw NotImplementedError("Demo only")
}
A reified wrapper can be nice, but it should improve the API, not hide important behavior.
Comparisons
| Concept | What it does | Runtime type access | Typical use |
|---|---|---|---|
Normal generic fun <T> | Uses a type parameter for compile-time safety | Usually no | Data processing, collections, reusable functions |
inline fun <reified T> | Makes the concrete type available in inline-generated code | Yes, for many type operations | Type checks, casts, class lookup, wrappers around reflection |
Passing KClass<T> manually | Caller provides the type explicitly | Yes | Constructors, factories, reflection-heavy APIs |
reified vs explicit class parameter
fun <T : Any> create(type: kotlin.reflect.KClass<T>): T {
throw NotImplementedError("Demo only")
}
inline fun <reified T : Any> create(): T {
return create(T::class)
}
Trade-off
-
Explicit
KClassparameter- clearer about where the runtime type comes from
- works without
inline - useful when the type must be passed around
-
reified- cleaner call site
- less boilerplate
- only available in inline functions
reified vs reflection
reified is not the same as full reflection.
reifiedmakes the actual type argument available in certain runtime expressions- reflection is a larger mechanism for inspecting classes, properties, annotations, and more
reified often helps you reach type information more conveniently, but it is not itself a full reflection system.
Cheat Sheet
Quick rules
- Generic types in Kotlin are usually erased at runtime.
- Use
reifiedwhen you need the actual typeTwhile the program is running. reifiedonly works on inline functions.- Common uses:
T::classT::class.javavalue is Tvalue as? T
Core syntax
inline fun <reified T> example(value: Any): Boolean {
return value is T
}
Without reified
Pass the type explicitly:
fun <T : Any> example(type: kotlin.reflect.KClass<T>) {
println(type.simpleName)
}
Use reified when
- you want cleaner APIs around class-based operations
- you need runtime checks involving
T - you are building utility functions
Do not use reified when
- the function only returns or forwards
T - no runtime type inspection is needed
- inlining would add unnecessary complexity
Remember
fun <T> ok(value: T): T = value
inline fun <reified T> needed(value: Any) = value is T
Limitation
reified helps with runtime access to the type parameter in an inline function, but it does not remove all generic type erasure limitations in every situation.
FAQ
Why does my Kotlin generic function work without reified?
Because many generic functions only need compile-time type safety, not runtime type inspection. If you are not doing is T, as? T, or T::class, reified may not be needed.
Why does reified require inline in Kotlin?
Because Kotlin makes reified work by copying the function body to each call site and replacing T with the actual type used there.
Can I use reified in a normal class method without inline?
No. A type parameter can only be reified in an inline function.
Is reified the same as reflection?
No. It is a way to preserve access to a generic type argument in inline functions. It can help with reflection-like tasks, but it is not full reflection by itself.
When should I use reified instead of passing Class or KClass?
Use reified when you want a cleaner helper function and the function can reasonably be inline. Use explicit class parameters when the type must be passed around or when inlining is not appropriate.
Can reified fully preserve List<String> at runtime?
Not in the simple sense many beginners expect. It helps with access to the type parameter, but nested generic details still have runtime limitations.
Does reified make code slower?
Not necessarily, but inline functions duplicate code at call sites. This is usually fine for small utility functions, which is where reified is most commonly used.
Mini Project
Description
Build a small type-filtering utility for a mixed list. This demonstrates exactly why reified is useful: the function needs to keep only items of a requested type at runtime.
Goal
Create a reusable Kotlin function that returns only values of a specific type from a mixed collection.
Requirements
- Create an
inlinefunction with areifiedtype parameter. - Accept a
List<Any>as input. - Return a new list containing only items of type
T. - Demonstrate the function with at least two different target types.
- Print the results clearly.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.