Access modifiers, also known as visibility modifiers, are keywords in Kotlin that control the visibility and accessibility of declarations like classes, objects, interfaces, functions, properties, and constructors.
They enforce encapsulation, modularity, and abstraction, helping to create more secure, organized, and maintainable code.
Kotlin offers four visibility modifiers: public, private, protected, and internal.
public
public is the default visibility in Kotlin. If you don't specify any modifier, the declaration is public.
-
Accessibility: Accessible from anywhere in the codebase.
-
Use case: Use
publicfor elements that are part of your public API and should be accessible globally. -
**Example:**kotlin
// Top-level function, public by default fun sayHello() { println("Hello, World!") } class Example { // Public property and method, visible everywhere val myProperty: String = "Public Property" fun myMethod() { println("Public Method") } }Use code with caution.
private
The private modifier restricts visibility to the containing scope, which can be a class or a file.
-
Accessibility:
- Class members: A
privatemember within a class is only accessible from within that class. - Top-level declarations: A
privatedeclaration (function, property, or class) in a file is only accessible from within the same file.
- Class members: A
-
Use case: Ideal for encapsulating implementation details that should not be exposed to the outside world. This prevents unintended modification of internal state.
-
**Example:**kotlin
// This top-level variable is only visible within this file private const val GREETING = "Hi" class EncapsulatedData { // This property is only accessible from inside this class private var secretKey: String = "verySecret" fun getSecret(): String { // Accessible here, but not outside the class return secretKey } }Use code with caution.
protected
The protected modifier is similar to private, but also allows visibility to subclasses. It cannot be used for top-level declarations.
-
Accessibility: A
protectedmember is visible only within its own class and its subclasses. -
Use case: Designed for inheritance scenarios where you want to expose certain members to child classes for overriding or reuse, but not to unrelated code.
-
Note on Kotlin vs. Java: In Java, a
protectedmember is also accessible from other classes in the same package. Kotlin does not have this behavior, making itsprotectedmodifier more restrictive and secure. -
**Example:**kotlin
open class Shape { protected val shapeName: String = "Shape" } class Circle : Shape() { fun printShapeName() { // Can access 'shapeName' because Circle is a subclass of Shape println("The shape name is: $shapeName") } } fun main() { val circle = Circle() circle.printShapeName() // Works // val shape = Shape() // println(shape.shapeName) // ERROR: shapeName is protected }Use code with caution.
internal
The internal modifier limits visibility to the same module, which is a set of Kotlin files compiled together.
-
Accessibility: Accessible from any declaration within the same module.
-
Use case: Useful in multi-module projects or libraries to expose APIs within the library while hiding internal implementations from external users.
-
**Example:**kotlin
// In Module A internal class InternalService { fun doInternalWork() { println("Performing internal task...") } } // In Module A class PublicApi { fun executeWork() { InternalService().doInternalWork() } } // In Module B (different module) // val service = InternalService() // ERROR: InternalService is internal to Module AUse code with caution.
Visibility with constructors and properties
Access modifiers can also be applied to constructors and property setters for finer control.
-
Constructors: By default, constructors have the same visibility as their class. To change this, use the
constructorkeyword explicitly.kotlinclass InternalClass internal constructor() // Constructor is internal class PrivateFactory private constructor() { companion object { fun create(): PrivateFactory { return PrivateFactory() } } }Use code with caution.
-
Property Setters: A property's setter visibility can be set separately from its getter.kotlin
var myProperty: String = "value" private set // Setter is private, getter is publicUse code with caution.
Nested vs. inner classes
Nested and inner classes have distinct visibility rules. A nested class's private members are not accessible to the outer class. An inner class, however, can access the outer class's members because it holds a reference to an outer class instance.
Summary of visibility scope
| Modifier | Top-Level Declarations | Class Members | Constructor |
|---|---|---|---|
public |
Visible everywhere | Visible everywhere | Visible everywhere the class is visible |
private |
Visible only within the same file | Visible only within the containing class | Visible only within the containing class |
protected |
Not allowed | Visible within the containing class and its subclasses | Visible within the containing class and its subclasses |
internal |
Visible only within the same module | Visible to any client in the same module | Visible only within the same module |