Explain how the inferred type of an object expression with no declared supertype behaves when it leaks out of a function, and why extra members may become inaccessible.
answer
- Anonymous type can't be named in public API
- Local/private -> full type kept
- Public/internal return -> widens to supertype or Any
- Extra members vanish from callers
- Fix: declare an interface supertype
basics
~20 sIf an anonymous object has no named supertype, its special members are only usable nearby. Once you return it from a public function, the visible type drops to the supertype (or Any), so the extra members disappear from the caller's view.
solid answer
~40 sAn object expression's class is anonymous, so it cannot be named in a public signature. Kotlin therefore restricts where the anonymous type is exposed. When the object is used as a **local variable** or a **private** function/property, the compiler keeps the precise anonymous type, and all its declared members are accessible. But when it escapes through a **public or internal** (non-private) function's return type or a non-private property, the inferred exposed type **collapses to the nearest declared supertype** — or to `Any` if there is none — because the anonymous type has no name a caller could use. Consequently any members not present on that supertype become invisible outside. The fix is to declare an explicit supertype (interface/abstract class) that carries the members you want callers to see, or to keep the object private/local.
code
kotlin · 9 linesclass Registry {
private fun secret() = object { val token = "abc" }
fun exposed() = object { val token = "abc" } // return type inferred as Any
fun use() {
println(secret().token) // OK
// println(exposed().token) // compile error: Any has no 'token'
}
}go deeper
Likely unaware; may just know the object has fields.
Recognizes that returning the object can hide members but may not articulate the local/private vs public rule.
States the precise rule: full type for local/private, widening to supertype or Any for non-private exposure, and the interface-based fix.
Uses this to shape API contracts — exposing intentional interfaces rather than leaking anonymous shapes — and anticipates refactor pitfalls across module boundaries.
## The core rule The class created by an object expression is **anonymous** — it has no usable name. So Kotlin limits how far that precise type travels: - As a **local** declaration or a **private** member, the compiler retains the full anonymous type, and **all** members (including ones not on any supertype) are accessible. - As a **non-private** (public/internal) function return or property, the exposed type is the **declared supertype**, or **`Any`** if none was declared. Members not on that type are then **inaccessible** to callers. ## Why A public API must name its types. An anonymous type can't be named at the call site, so the compiler widens the exposed type to something nameable. ```kotlin class C { // private -> precise anonymous type retained, x is visible private fun makePrivate() = object { val x = 1 } // public -> return type widens to Any, x is NOT visible to callers fun makePublic() = object { val x = 1 } fun demo() { println(makePrivate().x) // OK: 1 // println(makePublic().x) // ERROR: unresolved reference 'x' (type is Any) val local = object { val y = 2 } println(local.y) // OK: local keeps the anonymous type } } ``` ## Fix it by naming a supertype Give the object a declared supertype that carries the members you need exposed: ```kotlin interface HasValue { val value: Int } fun makePublic(): HasValue = object : HasValue { override val value = 1 } // caller can read .value because HasValue names it ``` ## Mental checklist - **Local or private** -> full anonymous type, extra members visible. - **Public/internal return or property** -> widens to supertype or `Any`. - Need extra members in the public API? **Declare an interface/abstract class** and expose that. This is a frequent source of "unresolved reference" confusion when refactoring a local anonymous object into a returned value.
- How do you make the extra members of a returned anonymous object accessible to callers?Declare an explicit supertype (interface or abstract class) that defines those members and return that supertype, so the members are nameable at call sites.
- Does this restriction apply to local variables?No. Local and private uses keep the precise anonymous type, so all declared members remain accessible there.
saying these in an interview costs you the question
- Believing a public function can return the full anonymous type with its extra members
- Not knowing the exposed type widens to Any when no supertype is declared
- Confusing this with a runtime error rather than a compile-time type widening
- Thinking the rule applies to local variables too
- Suggesting reflection as the normal fix instead of declaring a supertype