Skip to main content
In Kotlin, an open class is a class explicitly marked with the open modifier to permit inheritance. Because Kotlin classes are final by default, the compiler restricts subclassing unless the open keyword is applied.

Member Modifiers and Overriding

Marking a class as open does not implicitly apply the open modifier to its members. All properties and functions within an open class remain final by default. To allow a subclass to override a specific property or function, the member itself must also be explicitly marked with the open modifier.

The override Modifier and final Re-sealing

When a member is overridden in a subclass, the overriding member remains implicitly open to any further subclasses. To terminate the inheritance chain for a specific member while keeping the class itself open, you must explicitly apply the final modifier to the overridden member.

Interactions with Other Class Types

The open modifier has specific compilation rules when combined with other Kotlin class declarations:
  • abstract Classes: Abstract classes inherently permit inheritance. Applying the open modifier to an abstract class or an abstract member is redundant and results in a compiler warning.
  • data Classes: A data class cannot be marked open. The Kotlin compiler strictly enforces data classes as final to guarantee the integrity of compiler-generated methods (equals(), hashCode(), copy()).
  • sealed Classes: Sealed classes are implicitly abstract (which inherently allows restricted inheritance). They cannot be explicitly marked open because the sealed and open modifiers are mutually exclusive.
  • Interfaces: Interfaces and all of their non-private members (including those with default implementations or bodies) are implicitly open.

Safety and Compiler Implications

Initialization Order Hazards

Calling an open function or property from a base class constructor or init block is a major anti-pattern in Kotlin. When a base class is instantiated, its initialization logic executes before the derived class’s initialization. If the base class constructor invokes an open member that is overridden in the derived class, the overridden implementation will execute before the derived class’s state is fully initialized. This frequently leads to NullPointerExceptions or undefined behavior.

Smart Casting Restrictions

Marking a property as open (e.g., open val) disables Kotlin’s smart casting capabilities for that specific property. The compiler cannot guarantee the type or nullability of an open property because a subclass could override it with a custom getter that returns different values or types on subsequent invocations.
Tired of Poor Kotlin Skills? Fix That With Deep Grasping!Learn More