skip to content

Under Explicit API strict mode, which declarations get flagged and which are exempt? Give concrete examples.

level: middleimportance: should knowfreq 30%

answer

  1. Only public + protected are checked
  2. Two triggers: no visibility keyword, inferred return type
  3. Exempt: private, internal, local, overrides
  4. Constructor val/var needs explicit visibility
  5. Override signature is inherited, so untouched

basics

~10 s

It flags things visible outside your module — public and protected members that don't say their visibility, and public functions/properties with no written return type. Private, internal, local code, and overrides are left alone.

solid answer

~40 s

Strict mode scrutinizes only the API surface: public and protected declarations. It raises an error when such a declaration lacks an explicit visibility modifier (relying on the implicit public default), and when a public/protected function or property body relies on an inferred return type rather than a declared one. Exempt: private and internal declarations (not part of the API), local declarations inside function bodies, and overrides — an override inherits the supertype's signature, so its return type is already fixed and need not be repeated. Property accessors and primary-constructor val/var parameters follow the same rule as the property they back. Constant or trivially-typed cases still require the annotation in strict mode if they are public. The net effect: every symbol a consumer can see carries a deliberate visibility keyword and a spelled-out type.

code

kotlin · 9 lines
kotlin
// FLAGGED (public default + inferred type)
fun size(s: String) = s.length

// FIXED
public fun size(s: String): Int = s.length

// EXEMPT
private fun guard() {}
override fun toString() = "x" // inherited signature

go deeper

for a junior

Knows public stuff is checked and private/internal is left alone.

for a middle

Lists both triggers (visibility + return type) and the exemptions including overrides and locals.

for a senior

Explains why overrides and internal are exempt in terms of API surface, and handles constructor properties.

for a principal

Reasons about how the rule set keeps signal high and noise low across a large public API, and how it interacts with KMP/expect-actual surfaces.

## The mental model Explicit API mode only cares about what **leaves your module** — i.e., your **API surface**. That means **`public`** and **`protected`** declarations. Everything that is invisible to consumers is exempt. ## Flagged in strict mode 1. **Missing explicit visibility on a public/protected member** — relying on the implicit `public` default. 2. **Inferred return type on a public/protected function or property** — no declared type. ```kotlin // FLAGGED: no visibility keyword (defaults to public) and inferred return type fun total(items: List<Int>) = items.sum() // OK public fun total(items: List<Int>): Int = items.sum() ``` ## Exempt - **`private` and `internal`** declarations — not part of the public API at all. - **Local declarations** (functions/vals declared inside a function body). - **Overrides** — `override fun foo(): Int` inherits its signature from the supertype, so the type is already pinned; you don't have to restate visibility/type purely to satisfy the mode. - Property **getters/setters** are governed by the property's own visibility. ```kotlin class Cache { private fun evict() {} // exempt: private internal fun warmUp(): Unit {} // exempt: internal fun load(): String { // FLAGGED: missing 'public' keyword on a public member fun normalize() = "x" // exempt: local return normalize() } } interface Loader { fun load(): String } class FileLoader : Loader { override fun load() = "file" // exempt: override (signature inherited) } ``` ## Constructor properties A `val`/`var` in a **primary constructor** is a public property by default, so in strict mode it must carry an explicit visibility: ```kotlin class User(public val id: Long, internal val token: String) ``` ## Why these exemptions make sense The whole point is curating the **consumer-visible** surface. `private`/`internal` aren't visible, locals don't escape, and overrides have a signature dictated by the supertype — flagging any of those would be noise without protecting the API.

  • Why are overrides exempt from the return-type requirement?
    An override's signature is fixed by the supertype, so the type is already explicit and stable; restating it adds nothing.
  • Is a primary-constructor `val id: Long` flagged?
    Yes if it's effectively public — you must write the visibility explicitly, e.g. public val id: Long, or make it internal/private.

saying these in an interview costs you the question

  • Claiming private declarations get flagged
  • Thinking overrides must restate their return type to satisfy the mode
  • Saying it checks function parameters' types (those are always explicit anyway)
  • Believing internal members count as API surface
  • Not realizing constructor val/var are public properties

context