Open classes and inheritance: named parameters and default values; open and abstract; interface; getters and setters; visibility of properties, methods and class

Kotlin classes are final by default: you must mark a class and its members open to allow inheritance and overriding, the safe opposite of Java, plus abstract classes, interfaces, custom accessors, and default parameter values.

12 min read · 9 cards · 2 checks

Read in: English · हिन्दी · ગુજરાતી


Theory

The class that refused to be extended

A Java programmer's first Kotlin inheritance attempt usually fails with a confusing error. You write class Attendee : Person(), and Kotlin complains that Person is FINAL and cannot be inherited.

This is Kotlin's most surprising OOP rule: classes are final by default. To allow inheritance, you must explicitly mark the parent open, the exact OPPOSITE of Java, where everything is inheritable unless you write final. It is a deliberate safety choice. This lesson covers Kotlin inheritance, abstract classes, interfaces, property accessors, visibility, and default parameter values, completing FestConnect Mobile's object model.

Theory

Inheritance: open is required

In Kotlin, to inherit from a class, BOTH must cooperate:

  • the parent class must be marked open: open class Person(val name: String)
  • a member you want to override must ALSO be open
  • the child uses override: override fun describe()

Syntax: class Attendee(name: String) : Person(name) (a colon, and the parent constructor is called).

This is the REVERSE of Java (BCA403), where classes and methods are open by DEFAULT and you write final to CLOSE them. Kotlin's default is final (closed); you opt IN to inheritance. The reasoning: inheritance should be a deliberate design decision, not an accident, so the safe default is 'cannot be extended unless intended'.

Practical

open class, override, and a default parameter

// Parent must be OPEN to be inherited:
open class Person(val name: String) {
    open fun describe() = "Person: $name"   // open to allow override
}

// Child inherits with : and calls the parent constructor:
class Attendee(name: String, val event: String) : Person(name) {
    override fun describe() = "Attendee $name for $event"   // override
}

// DEFAULT PARAMETER VALUE: role defaults to "guest"
fun register(name: String, role: String = "guest") =
    "$name registered as $role"

fun main() {
    val p: Person = Attendee("Riya", "Garba")
    println(p.describe())            // Attendee Riya for Garba
    println(register("Aman"))        // Aman registered as guest (default)
    println(register("Zoya", "vip")) // Zoya registered as vip
}

Theory

Abstract, interfaces, accessors, visibility, defaults

The rest of Kotlin's OOP toolkit, mostly familiar:

  • abstract classes: cannot be instantiated; abstract members must be overridden (like Java)
  • interfaces: interface, with abstract members AND default implementations; a class can implement several
  • getters/setters: properties have default accessors; you can write a custom get()/set(value) with a backing field when you need logic
  • visibility: public (default), private, protected, and internal (visible in the same MODULE, a level Java lacks)
  • default parameter values: fun greet(name: String = "Guest") lets callers omit arguments, reducing the need for overloaded functions

Default values pair beautifully with named parameters (last lesson): one flexible function replaces many overloads.

Quiz

You write class Attendee : Person() but get an error that Person cannot be inherited. Why, and what fixes it?

  1. Person needs a constructor; add one
  2. Kotlin classes are FINAL by default; mark the parent 'open class Person' to allow inheritance (the opposite of Java)
  3. You must use the 'extends' keyword, not a colon
  4. Kotlin does not support inheritance
Show the answer

Kotlin classes are FINAL by default; mark the parent 'open class Person' to allow inheritance (the opposite of Java)

Kotlin classes are FINAL (closed) by DEFAULT, so a plain class Person cannot be inherited; you must explicitly mark it open class Person to allow it (and open on any member you want to override). This is the REVERSE of Java, where classes are open by default and you write final to close them. Option A misdiagnoses it (the issue is finality, not a missing constructor). Option C is wrong: Kotlin uses a colon (: Person()) for inheritance, not extends. Option D is false: Kotlin fully supports inheritance, it just requires opting IN. The design reason: inheritance should be intentional, so the safe default prevents accidental extension. Remember: open to inherit, override to override.

Think first

Why is 'final by default' the safer choice?

Java classes are open (inheritable) by default; Kotlin classes are final (closed) by default. Why does Kotlin consider its default safer? Then tap.

Show the answer

Because inheritance is a POWERFUL but RISKY relationship: a subclass depends on its parent's internal behaviour, and if the parent was not DESIGNED to be extended, a subclass can break in subtle ways when the parent changes (the 'fragile base class' problem). Java's open-by-default means EVERY class is inheritable whether or not its author intended it, so subclasses can extend classes never meant to be extended, creating hidden, brittle dependencies. Kotlin's final-by-default flips this: a class is closed unless its author DELIBERATELY marks it open, signalling 'I designed this to be extended safely'. So inheritance becomes an intentional, documented decision, not an accident. This follows the principle 'design for inheritance or prohibit it'. You lose nothing (mark open when you mean it) and gain safety: no accidental, fragile subclassing. The safe default is the one that requires you to opt into the risky feature.

Watch out

Kotlin inheritance traps

Forgetting open: classes and members are final by default; mark them open to inherit/override (the Java-reflex bug).

extends/implements: Kotlin uses a colon (:) for both inheritance and interface implementation, not those keywords.

Calling the parent constructor: : Person(name) calls it; forgetting the arguments errors.

override keyword required: you must write override on an overriding member (Kotlin will not let you accidentally shadow).

internal vs private: internal is module-wide, private is class/file: know the difference (internal is new vs Java).

Theory

Unit 2 complete: Kotlin OOP mastered

FestConnect Mobile now has a full Kotlin object model: concise classes, safe inheritance (open by intent), abstract classes, interfaces, custom accessors, and default parameters. That is the language, complete. Unit 3 turns to actually BUILDING the Android app: parsing JSON, moving between screens with intents, storing data in SQLite, and using device features like location and the camera. From the language to a real, data-driven, multi-screen mobile app.

Summary

Key takeaways

  • Kotlin classes are FINAL by default: mark the parent 'open' to allow inheritance, and members 'open' to override (opposite of Java).
  • Inherit with a colon: class Attendee(name) : Person(name); the child uses 'override' on overridden members.
  • abstract classes cannot be instantiated; interfaces can have abstract members and default implementations, and a class can implement several.
  • Properties have default getters/setters; write custom get()/set(value) with a backing field for logic.
  • Visibility: public (default), private, protected, internal (same module: new vs Java).
  • Default parameter values (fun greet(name: String = "Guest")) let callers omit arguments, reducing overloads; pair with named parameters.
  • Memory hook: final by default, open to inherit, override to override: inheritance is opt-in.

Study this properly

This page is the lesson to read. In Gri-Learn the same topic is a graded deck: the self-checks are scored and your weak topics are tracked. Free to start.

Start this topic

Already have an account? Sign in

More from OOPS Concepts with Kotlin

Gri-Learn · syllabus-mapped B.C.A. lessons in English, Hindi and Gujarati