An inline function with a reified parameter also takes a lambda. How do `noinline`/`crossinline` and the inlining contract interact with reified, and what constrains where you can call such a function?
answer
- reified needs body inlined, not lambdas
- noinline = real Function object, still allows reified
- crossinline = no non-local return across context
- no ::reifiedFn / no reflection / no Java caller
- @PublishedApi for internal access
basics
~20 sReified needs the whole function inlined. Lambda parameters are inlined too by default; marking one noinline keeps that lambda as a real object but doesn't break reified, since the function body is still copied. crossinline forbids non-local returns from the lambda.
solid answer
~50 s`reified` depends only on the **function body** being inlined at the call site; it is independent of how each lambda parameter is treated. By default all lambda params of an `inline` function are themselves inlined. `noinline` opts a specific lambda out (so it becomes a real `Function` object you can store/pass on) — that's fine alongside reified. `crossinline` keeps the lambda inlined but bans **non-local returns** from it, needed when the lambda is invoked from another execution context (e.g. inside a `Runnable`). Because the function is `inline`, callers must be code the compiler can inline into: you cannot call a reified-inline function via reflection, you cannot reference it as a function value (`::fn`), and Java callers can't use the reified parameter. Public inline functions also can't touch `private`/`internal` members of the declaring class unless those are `@PublishedApi internal`, because the body is copied into foreign call sites.
code
kotlin · 7 linesinline fun <reified T> register(noinline factory: () -> T) {
registry[T::class] = factory // reified T works; factory kept as object
}
inline fun <reified T> runLater(crossinline body: () -> T) {
executor.execute { log(T::class, body()) } // crossinline: no non-local return
}go deeper
Recognizes that lambdas in inline functions are inlined by default and that reified relates to the type parameter, not the lambda.
Distinguishes noinline (real object) from crossinline (no non-local return) and confirms neither breaks reified.
Explains the full inline contract — no reflection/function-ref, Java unusable, @PublishedApi — and why reified depends only on the body being inlined.
Reasons about API surface design: when to expose reified-inline helpers vs. Class<T> overloads for Java interop, and the bytecode/ABI implications of @PublishedApi.
## Two independent axes When you have `inline fun <reified T> withX(block: () -> Unit)`, there are two separate concerns: 1. **The body is inlined** — this is what `reified` requires (T is substituted at the call site). 2. **Each lambda parameter is inlined** — separately controllable per lambda. Reification depends only on #1, so changing how a lambda is treated never disables reified. ## `noinline` By default every lambda param of an inline function is inlined (no `Function` object allocated, non-local return allowed). Mark a param `noinline` when you need it to behave as an ordinary value — store it, return it, or pass it to a non-inline function: ```kotlin inline fun <reified T> register(noinline factory: () -> T) { // factory is a real Function0<T>; can be stored in a map registry[T::class] = factory } ``` Here `reified T` still works (`T::class`) even though `factory` is not inlined. ## `crossinline` Use `crossinline` when a lambda is still inlined but is invoked from a **different execution context** (a nested lambda, a `Runnable`, another thread). It forbids non-local `return` out of the enclosing function, which would be unsafe across that boundary: ```kotlin inline fun <reified T> runLater(crossinline body: () -> T) { executor.execute { val value: T = body(); log(T::class, value) } } ``` ## Constraints the inline contract imposes Because the body (with T substituted) is copied into each call site, reified-inline functions inherit all inline limitations: - **No function reference / reflection**: you can't do `::register` or call it reflectively — there's no single shared method to point at when reified. - **Not callable from Java**: Java sees an erased generic with no reified substitution. - **`@PublishedApi`**: a `public` inline function calling `internal`/`private` members must expose them via `@PublishedApi internal`, since the body executes inside callers in other modules. - **Code size**: each call site duplicates the (possibly large) body — keep these functions thin. ## Bottom line `reified` and `noinline`/`crossinline` are orthogonal knobs: the first is about substituting `T` (needs the body inlined), the latter two are about how individual lambdas are emitted and which returns are legal.
- Can you take a function reference (`::myReifiedFn`) to an inline reified function?No. Reified functions exist only as inlined call-site copies; there is no single callable method to reference, so `::` and reflection are disallowed.
- Why might a public inline reified function need `@PublishedApi internal`?Its body is copied into call sites in other modules, so any `internal` members it touches must be marked `@PublishedApi internal` to remain accessible there.
saying these in an interview costs you the question
- Claiming noinline or crossinline disables reified
- Saying you can call a reified inline function via reflection or as a function reference
- Believing crossinline is needed for reified to work
- Ignoring @PublishedApi and accessing private members from a public inline body
- Thinking Java callers can use the reified type parameter