Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

In Kotlin, properties are final by default in classes. An open property is a property that permits subclasses to override its accessors (getters and setters). To make a class property overridable, it must be explicitly marked with the open modifier. Conversely, properties declared within interfaces are implicitly open and do not require the open modifier. To override an open property, the subclass must use the override modifier.
interface Identifiable {
    val id: String // Implicitly open
}

open class Base : Identifiable {
    override val id: String = "BaseID"
    open var mutableProperty: Int = 0 // Explicitly open
}

class Derived : Base() {
    override val id: String = "DerivedID"
    override var mutableProperty: Int = 1
}

Accessors and Backing Fields

When overriding an open property, you are overriding its accessors, not its backing field. Backing fields themselves cannot be overridden. If a superclass property has a backing field and a subclass overrides that property with its own initializer, the compiler generates a new backing field specifically for the subclass. The superclass’s backing field is still generated and initialized by the superclass constructor. Subclasses can access the superclass’s property implementation (either its backing field value or its custom accessor logic) using the super keyword.
open class Entity {
    open val status: String = "Active" // Backing field initialized to "Active"
}

class AuditedEntity : Entity() {
    // Overrides the getter; no new backing field is generated here
    override val status: String
        get() = super.status + " (Audited)"
}

Initialization Order Hazards

Accessing an open property within a superclass constructor or init block is inherently unsafe. During object instantiation, the superclass constructor executes before the subclass constructor. If the superclass constructor reads an overridden open property, it invokes the subclass’s getter. Because the subclass has not yet been initialized, this will return an uninitialized value (such as null, 0, or false depending on the type), often leading to NullPointerExceptions or corrupted state.
open class Parent {
    open val size: Int = 0
    init {
        // DANGER: Reads 'size' before Child initializes it. 
        // This prints 0, not 10.
        println("Size is: $size") 
    }
}

class Child : Parent() {
    override val size: Int = 10
}

Overriding Rules, Mutability, and Type Variance

The compiler enforces strict rules regarding property mutability and type variance when overriding open properties:
  • Mutability Expansion (val to var): You can override a val property with a var property. A val property contract only guarantees the existence of a getter. Overriding it with a var fulfills the getter contract while appending a setter.
  • Mutability Restriction (var to val): You cannot override a var property with a val property. A var establishes a contract for both a getter and a setter; a subclass cannot remove the setter contract defined by its superclass.
  • Type Covariance for val: When overriding a val property, the overriding property’s type can be the same as, or a subtype of, the overridden property’s type.
  • Type Invariance for var: When overriding a var property, the overriding property’s type must be exactly the same as the overridden property’s type. Because a var includes a setter, allowing a subtype would violate type safety by restricting the types of values that could be assigned to the superclass reference.
open class SuperType {
    open val id: Number = 0
    open var name: CharSequence = "Unknown"
}

class SubType : SuperType() {
    // Valid: 'val' overridden by 'var', and 'Int' is a subtype of 'Number' (Covariant)
    override var id: Int = 1 
    
    // INVALID: Cannot override 'var' with 'val'
    // override val name: CharSequence = "Known" 
    
    // INVALID: 'var' must be invariant. Cannot change 'CharSequence' to 'String'
    // override var name: String = "Known"
    
    // Valid: Exact type match for 'var'
    override var name: CharSequence = "Known"
}

Primary Constructor Overrides

Open properties can be overridden directly within the primary constructor of a subclass. This provides a concise syntax to declare the property and initialize its new backing field simultaneously.
open class Shape {
    open val vertexCount: Int = 0
}

class Rectangle(override val vertexCount: Int = 4) : Shape()

Terminating the Override Chain

An overridden property is implicitly open to further subclasses. To prevent deeper subclasses from overriding the property again, the final modifier must be explicitly applied alongside override.
open class Intermediate : Base() {
    final override val id: String = "LockedID"
}
Master Kotlin with Deep Grasping Methodology!Learn More