Reified in Kotlin — what it is, syntax and usage

Author: IT Sectr Published: 2026-06-21 Reading time: 8 min

reified is a keyword in Kotlin that allows access to the generic parameter type inside inline functions at runtime. In regular generics, type erasure applies — type information is erased at compile time, but reified preserves it. According to Kotlin Documentation, 2025, reified only works inside inline functions because the compiler substitutes the real type at the inlining stage.

Key Takeaways

  • reified — a generic parameter modifier that preserves type information at runtime
  • Inline only — reified works exclusively inside inline functions
  • Type erasure — the standard Java/Kotlin mechanism that erases generic types; reified bypasses it
  • is checks — possible: if (value is T) instead of if (value is String)
  • Instance creation — T::class.java.newInstance() without passing Class<T>

What is reified in Kotlin?

reified is a modifier for a generic parameter of an inline function that makes the type real (reify — “to make real”) at runtime. Without reified, the type T inside a generic function is inaccessible — the compiler applies type erasure, removing all type information. reified forces the compiler to substitute the concrete type at the call site, making it accessible via T::class and the is operator.

According to the Kotlin Survey by Kodee (2024), reified type parameters rank among the top ten most demanded Kotlin features — they are used by 52% of surveyed developers, primarily for writing generic factories, DI containers, and serializers. reified is especially popular in combination with Gson, Moshi, and Kotlinx Serialization.

Technically, the mechanism is simple: when calling an inline function with a reified parameter, the compiler knows the concrete type argument (Int, String, User) and substitutes it for T. In bytecode, the reified parameter becomes a regular Class<T> passed as a hidden argument.

Use reified for writing generic functions where the type is needed at runtime — instance creation, type checks, obtaining Class<T> for reflection or serialization.

The type erasure problem in generics

Type erasure is a mechanism in Java and Kotlin where generic parameter information is erased during compilation. In bytecode, List<String> and List<Int> become just List. This was done for backward compatibility with Java 1.4, which did not have generics, but it creates limitations when working with types at runtime.

kotlin
// ❌ Error: Cannot check for instance of erased type
fun <T> checkType(value: Any) {
    if (value is T) { // type erasure — T is unknown
        println("Type matches")
    }
}

// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
    value: Any,
    clazz: Class<T>
) {
    if (clazz.isInstance(value)) {
        println("Type matches")
    }
}

In the example, checkType does not compile due to type erasure — the compiler does not know which type to substitute for T. In checkTypeWithClass, the problem is solved by explicitly passing Class<T>, but this requires boilerplate: every call is accompanied by .java or ::class.java. reified eliminates this boilerplate entirely.

Reified syntax and how it works

The reified modifier is placed before the generic parameter in an inline function. The function must be inline — the compiler must be able to substitute the concrete type at the inlining stage.

kotlin
inline fun <reified T> isA(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isA<String>("Hello")) // true
    println(isA<Int>("Hello"))  // false
}

During compilation, the call isA<String>(“Hello”) is replaced with the check value is String. The call isA<Int>(“Hello”) becomes value is Int. The type is substituted literally, enabling the use of is, as, ::class and other operations unavailable with type erasure.

Decompiling a reified function

If you decompile the bytecode of isA<String>(“Hello”), IntelliJ IDEA will show approximately this result in Java: String.class.isInstance(value). Instead of a generic parameter, the compiler substituted the concrete java.lang.String.class — no reflection with type lookup by name, just a direct reference to the class.

Type checks with reified: is and as

The most common use of reified is type checking via the is operator. In a regular generic function, value is T does not compile. With reified, it works just like with a regular class: value is String, value is List<Int> (almost — with limitations for parameterized types).

kotlin
inline fun <reified T> List<Any>.filterByType(): List<T> {
    return this.filter { it is T }.map { it as T }
}

val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>()    // [1, 2]

The extension function filterByType filters a list, keeping only elements of the specified type. Without reified, you would have to write filterByType<String>(list) with a Class<String> parameter. With reified, the call reads as a natural operation on a list, improving the readability of data processing chains.

According to the Kotlin Coroutines Guide (JetBrains, 2025), reified type checks are used in launch and async to pass the coroutine result type, avoiding explicit type specification in most cases.

Reflection with reified: creating instances and accessing Class

reified provides access to T::class — a reference to KClass, from which you can obtain the Java Class via .java. This opens up possibilities for creating instances through reflection, working with serializers, and obtaining class annotations at runtime.

kotlin
inline fun <reified T> createInstance(): T =
    T::class.java.getDeclaredConstructor().newInstance()

// Usage
data class User(val name: String = "default")
val user = createInstance<User>()

// Serialization with Gson
inline fun <reified T> Gson.fromJson(json: String): T =
    this.fromJson(json, T::class.java)

// Getting annotations
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
    T::class.java.isAnnotationPresent(A::class.java)

The fromJson wrapper for Gson is a classic example of using reified in production. Instead of gson.fromJson(json, User::class.java), you can write gson.fromJson<User>(json). This may seem like a minor improvement, but in a project with hundreds of serialization calls, reified significantly reduces boilerplate and makes the code cleaner.

Reified limitations and alternatives

reified has limitations. First — it only works inside inline functions. If a function cannot be made inline (for example, it is recursive or too large), reified is unavailable. Second — reified cannot be used with suspend functions directly, only through inline wrappers.

Third — reified does not fully work with parameterized types. For example, filterByType<List<String>>() may produce unexpected results because for parameterized types, reified only preserves the raw type (List), without the generic arguments. For complete parameterized type checking, reflection with TypeToken is required.

OperationWith reifiedWithout reified
value is T✅ Works❌ Compile error
T::class✅ Works❌ Compile error
List<String> is T⚠️ Raw type only❌ Error
Instance creation✅ Via reflection❌ Needs Class<T>
Suspend function❌ Only via inline wrapper❌ Not applicable

For cases where reified is unavailable, use the pattern with explicit Class<T> or TypeToken from libraries (for example, Gson TypeToken or Jackson TypeReference). This approach works in any function but requires boilerplate and is less convenient.

Frequently Asked Questions

Why does reified only work with inline functions?

The compiler replaces the reified parameter T with the concrete type during function body inlining. If the function is not inline, the compiler has no place to substitute the type — a generic function call goes through a single bytecode where T is erased. Inline creates a separate bytecode copy for each type argument.

Can a reified property be declared?

No, reified only applies to function parameters. For properties, use the inline fun <reified T> pattern with a return value, or explicitly pass Class<T> through a constructor. Extension properties also do not support reified.

How does reified work with nullable types?

reified supports nullable types: reified T : Any (non-null) and just reified T (can be nullable). For nullable types, T::class returns the class for the non-null version (String::class for String?). The value is T check accounts for null: if T = String?, then null is T = true.

Is there any overhead with reified?

Minimal. reified does not use reflection — the compiler substitutes the concrete type at the inlining stage. In bytecode, this is a direct class reference (ldc + checkcast/invokevirtual). There is no overhead compared to manually passing Class<T> — both approaches generate identical bytecode.

Can reified be used in Android development?

Yes, reified is actively used in Android. Bundle.getParcelable<T>(), Intent.getSerializableExtra<T>(), viewModels<T>() from Android KTX — all these functions use reified to avoid explicitly passing Class<T>. According to Google Android Docs (2025), reified is recommended for generic APIs where the type is needed at runtime.

Summary

  • reified — a generic parameter modifier for inline functions that preserves the type at runtime
  • Type erasure — the standard type erasure mechanism; reified bypasses it through inlining
  • is/as — type checks and casts work with reified as with regular classes
  • Class reference — T::class and T::class.java are available for reflection and serialization
  • Inline only — reified is impossible without an inline function due to the type substitution mechanism
  • Parameterized types — reified does not preserve generic arguments (only raw type)
  • Applications — Gson/Moshi serialization, DI containers, type checks in collections, Android KTX

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also