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, andinternal(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?
- Person needs a constructor; add one
- Kotlin classes are FINAL by default; mark the parent 'open class Person' to allow inheritance (the opposite of Java)
- You must use the 'extends' keyword, not a colon
- 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.