Compare `internal` and `private` for top-level declarations. What exactly is the scope of each?
answer
- private top-level = same FILE
- internal = same MODULE (Gradle source set / IDEA module)
- Order: private < internal < public
- internal enforced via JVM name mangling
- internal is encapsulation, not security
basics
~10 sinternal means visible to all code in the same module (your compiled unit). private on a top-level declaration means visible only inside the same file. Both hide the declaration from outside consumers.
solid answer
~40 sFor **top-level** declarations, `private` scopes visibility to the **same file** — other files in the same package or module cannot see it. `internal` scopes to the whole **module**: every file compiled together (a Gradle source set / IntelliJ module / Maven build) can use it, but code in other modules cannot, even if it depends on yours. So `private` < `internal` < `public` in breadth. The compiler enforces `internal` by name-mangling the JVM bytecode (appending a module hash to the method name) so other modules can't bind to it accidentally. A common use: expose `internal` helpers across a module's files while keeping `public` API minimal, and use top-level `private` for single-file implementation details.
code
kotlin · 9 lines// File A.kt
internal fun shared() = 42 // visible across the module
private fun onlyHere() = 1 // visible only in A.kt
// File B.kt (same module)
fun use() {
shared() // OK: internal, same module
// onlyHere() // ERROR: private to A.kt
}go deeper
Knows private hides things and internal is broader, even if fuzzy on file-vs-module.
Pins private to the file and internal to the module; gives the correct ordering and a use case for each.
Explains JVM name mangling for internal, the precise definition of a module (source sets), and that it's encapsulation not security.
Reasons about module decomposition, library API hygiene, and how internal plus explicit-API mode shape a maintainable public surface across modules.
## Scope definitions (top-level) - **`private`**: visible only within the **same source file**. Two different `.kt` files in the same package cannot see each other's top-level `private` declarations. - **`internal`**: visible within the **same module**. A *module* is a set of files compiled together — a Gradle source set (e.g. `main`), an IntelliJ module, a Maven project, or one Ant/CLI invocation. All files in that module can access an `internal` declaration; consumers in other modules cannot. - **`public`** (default): visible everywhere the file is on the classpath. ## Ordering ``` private (file) < internal (module) < public (everywhere) ``` `protected` doesn't apply to top-level declarations (it's class-members only) and is unrelated to this ordering for top-levels. ## How `internal` is enforced on the JVM The JVM has no native "module" access level, so the Kotlin compiler **mangles names**: an `internal` function compiles to a JVM method whose name carries a module-specific suffix (e.g. `helper$my_module_main`). This prevents code in another module — including Java callers — from binding to it by accident. It also means `internal` members are technically reachable via reflection or from Java with the mangled name, so it's an *encapsulation* tool, not a *security* boundary. ```kotlin // file: Repo.kt (module :data) internal fun mapRow(row: Row): User = User(row.id) // any file in :data sees it private fun debugDump(u: User) = println(u) // only Repo.kt sees it ``` ## Choosing between them - Use **`private`** top-level for a helper used by only one file — minimal surface, no name pollution. - Use **`internal`** to share across files of a module while hiding from downstream consumers (e.g. a library's implementation package). - Promote to **`public`** only what is genuinely part of the API. ## Common gotcha Developers expect top-level `private` to mean "this package." It does **not** — it's strictly the file. If you need cross-file-but-not-cross-module sharing, that's exactly `internal`.
- Exactly what counts as a 'module' for `internal`?A set of files compiled together: a Gradle/Maven source set or build, an IntelliJ module, or one kotlinc/CLI invocation. Test source sets can be a separate module from main.
- Can Java code in another module call a Kotlin `internal` function?Only by using the mangled name; it won't bind by the plain name. So `internal` is enforced encapsulation, not an absolute access lock — reflection/mangled names can still reach it.
saying these in an interview costs you the question
- Saying top-level `private` means package-scope
- Claiming `internal` means package-scope
- Thinking `internal` is a hard security boundary
- Not knowing what a 'module' means for `internal`
- Ordering the visibilities incorrectly