Kotlin file Keyword
last modified April 19, 2025
Kotlin's file keyword is used to specify visibility modifiers for top-level declarations. It controls access to declarations within the same file. This tutorial explores file-level visibility with practical examples.
Basic Definitions
The file visibility modifier in Kotlin makes declarations visible
only within the same source file. It's more restrictive than internal
but less restrictive than private for top-level declarations.
Basic File-private Visibility
The file keyword limits visibility to the current file. This is
useful for helper functions or classes that shouldn't be exposed outside.
package com.zetcode.utils
file fun calculateSquare(x: Int): Int {
return x * x
}
fun publicCalculateCube(x: Int): Int {
return x * x * x
}
fun main() {
println(calculateSquare(5)) // Accessible: 25
println(publicCalculateCube(3)) // Accessible: 27
}
Here calculateSquare is file-private and can only be used within
Utils.kt. publicCalculateCube is accessible from other files. Both
are accessible in the main function within the same file.
File-private Class
Classes can also be marked as file-private using the file modifier.
This is useful for implementation details that shouldn't leak outside the file.
package com.zetcode.db
file class DatabaseConnection(private val url: String) {
fun connect() = println("Connected to $url")
}
fun createConnection(): DatabaseConnection {
return DatabaseConnection("jdbc:mysql://localhost/mydb")
}
fun main() {
val conn = createConnection()
conn.connect() // Works within same file
}
The DatabaseConnection class is only visible within Database.kt. The
createConnection function can return it because they're in the same
file. External files can't directly create DatabaseConnection instances.
File-private Properties
Top-level properties can be file-private to share state within a file while keeping it hidden from other files. This is useful for module-level constants.
package com.zetcode.config
file const val MAX_RETRIES = 3
const val TIMEOUT = 5000L
fun shouldRetry(attempt: Int): Boolean {
return attempt < MAX_RETRIES
}
fun main() {
println(shouldRetry(2)) // true
println("Timeout: $TIMEOUT") // 5000
}
MAX_RETRIES is file-private while TIMEOUT is public.
Both are accessible within Config.kt, but only TIMEOUT can be
accessed from other files. This controls what implementation details are exposed.
File-private Extension Functions
Extension functions can be file-private to limit their scope. This prevents polluting the global namespace with extensions only needed locally.
package com.zetcode.utils
file fun String.encrypt(): String {
return this.reversed()
}
fun String.publicEncrypt(): String {
return encrypt() // Can call file-private function
}
fun main() {
val secret = "password".encrypt() // Works
println(secret) // Output: drowssap
}
The encrypt extension is only available in StringUtils.kt.
publicEncrypt can use it internally. This pattern keeps helper
extensions from being visible to other files while still being reusable within.
File-private Interfaces
Interfaces can be file-private to restrict their implementation to the current file. This is useful for internal contracts between components.
package com.zetcode.logging
file interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) {
println("[LOG] $message")
}
}
fun createLogger(): Logger {
return ConsoleLogger()
}
fun main() {
val logger = createLogger()
logger.log("Test message") // [LOG] Test message
}
The Logger interface is only visible within Logger.kt. External
files can use createLogger but can't implement Logger
directly. This enforces a controlled way of creating loggers.
File-private Companion Objects
Companion object members can be file-private to share implementation details between class methods while keeping them hidden from other files.
package com.zetcode.utils
class IdGenerator {
companion object {
file var counter = 0
file fun nextId() = ++counter
}
fun generate(): Int {
return nextId() // Can access file-private members
}
}
fun main() {
val gen = IdGenerator()
println(gen.generate()) // 1
println(gen.generate()) // 2
}
The counter and nextId are file-private but accessible
to all IdGenerator methods. This maintains state while preventing
external modification. Each call to generate increments the counter.
File-private and Internal Visibility
When comparing file with internal visibility,
file is more restrictive. internal makes declarations
visible to the whole module.
package com.zetcode.visibility
file val filePrivate = "File private"
internal val moduleVisible = "Module visible"
fun printValues() {
println(filePrivate) // Accessible
println(moduleVisible) // Accessible
}
fun main() {
printValues()
}
filePrivate is only visible in Visibility.kt while
moduleVisible can be accessed from any file in the same module.
Both are accessible within the file they're declared in.
Best Practices for File-private Declarations
- Hide implementation details: Use file-private for helpers that shouldn't be exposed outside the file.
- Reduce namespace pollution: Keep file-specific extensions and utilities file-private.
- Share state carefully: Use file-private properties for file-level state that shouldn't be modified externally.
- Document file-private APIs: Even though they're not public, document them for maintainers.
- Consider alternatives: For truly private declarations,
use
privatewithin classes or objects.
Source
Kotlin Visibility Modifiers Documentation
This tutorial covered Kotlin's file visibility modifier in depth.
We explored various use cases including functions, classes, properties, and
interfaces. Proper use of file-private visibility helps create clean, modular
code with well-controlled access to implementation details.
Author
List all Kotlin tutorials.