Kotlin Inheritance and Interfaces Explained
Kotlin's inheritance model is safer than Java's: classes are final by default and methods are non-overridable by default — Charlie must explicitly declare open to allow inheritance, with the compiler guarding polymorphism safety.
1. What You'll Learn
open/abstract: classes are final by default, requires explicit opening- Interface default implementations
overridemandatory annotation- Multiple-inheritance conflict resolution:
super<ParentA> - Charlie in action: BulkOrder / PriorityOrder inheritance hierarchy
2. An Architect's Real Story
(1) Pain Point: Accidental Override Causing Production Incidents
Bob inherited the Order class to create SpecialOrder and accidentally overrode the calculateTotal method — causing 500 special orders with incorrect amounts. The finance team raised an urgent alert.
(2) Kotlin's Compile-Time Guard
KOTLIN
// Java: override annotation is optional - silent override possible
class SpecialOrder extends Order {
double calculateTotal() { ... } // Silently overrides!
}
// Kotlin: override is MANDATORY - compiler catches mistakes
open class Order {
open fun calculateTotal(): Double = ...
}
class SpecialOrder : Order() {
override fun calculateTotal(): Double = ... // Must declare override
}
overridemandatory annotation + classes final by default = compiler guards inheritance safety. Bob will never accidentally override again.
3. Open Inheritance Model
(1) Classes Final by Default
KOTLIN
// Default: class is final (cannot be inherited)
class Order(val id: String) // Cannot be extended
// Open: explicitly allow inheritance
open class Order(val id: String) {
// Methods are also final by default
fun process() { } // Cannot be overridden
// Open method: explicitly allow override
open fun calculateTotal(): Double = 0.0
}
// Abstract: must be inherited, cannot be instantiated
abstract class BaseOrder(val id: String) {
// Abstract method: must be implemented by subclass
abstract fun calculateTotal(): Double
// Concrete method in abstract class
fun getId(): String = id
}
(2) Inheritance Modifier Hierarchy
| Modifier | Class | Method | Instantiable | Inheritable | Overridable |
|---|---|---|---|---|---|
| Default (final) | ✅ | ✅ | ✅ | ❌ | ❌ |
open |
✅ | ✅ | ✅ | ✅ | ✅ |
abstract |
✅ | ✅ | ❌ | ✅ (must) | ✅ (must) |
(3) Java vs Kotlin Inheritance Comparison
| Dimension | Java | Kotlin |
|---|---|---|
| Class default | Inheritable | final (non-inheritable) |
| Method default | Overridable | final (non-overridable) |
| Override annotation | @Override (optional) |
override (mandatory) |
| Open inheritance | No declaration needed | open explicit declaration |
| Design philosophy | "Open by default" | "Closed by default" |
4. Interfaces and Default Implementations
(1) Interface Definition
KOTLIN
interface Discountable {
// Abstract method
fun discountRate(): Double
// Default implementation
fun applyDiscount(total: Double): Double {
return total * (1.0 - discountRate())
}
}
interface Trackable {
val trackingCode: String? // Abstract property
fun track(): String = trackingCode?.let { "Tracking: $it" } ?: "Not shipped"
}
// Implement multiple interfaces
class PremiumOrder(
val id: String,
val total: Double,
override val trackingCode: String?
) : Discountable, Trackable {
override fun discountRate() = 0.15 // 15% discount
}
(2) Interface vs Abstract Class
| Dimension | Interface | Abstract Class |
|---|---|---|
| Constructor | None | Has |
| State | None (only abstract properties) | Has (concrete properties) |
| Multiple inheritance | Supported (multiple implements) | Not supported (single inherits) |
| Default implementation | Kotlin supported | Supported |
| Use case | Behavior contract | Shared state + behavior |
5. override Mandatory Annotation
(1) Basic Usage
KOTLIN
open class Order(val id: String, var status: String) {
open fun summary(): String = "Order $id: $status"
}
class BulkOrder(id: String, status: String, val minQuantity: Int) : Order(id, status) {
// MUST use override keyword
override fun summary(): String = "Bulk $id: $status (min: $minQuantity)"
}
(2) Preventing Further Override
KOTLIN
open class Order {
open fun process() { }
}
class SpecialOrder : Order() {
// Prevent further override in subclasses
final override fun process() { }
}
6. Multiple-Inheritance Conflict Resolution
(1) Diamond Inheritance Problem
KOTLIN
interface A {
fun hello() = "A"
}
interface B {
fun hello() = "B"
}
// Diamond: C inherits hello() from both A and B
class C : A, B {
// MUST override to resolve conflict
override fun hello(): String {
// Explicitly choose which parent's implementation
return super<A>.hello() // Choose A's implementation
}
}
(2) Conflict Resolution Strategies
KOTLIN
class D : A, B {
override fun hello(): String {
// Strategy 1: Pick one parent
return super<B>.hello()
// Strategy 2: Combine both
// return "${super<A>.hello()} + ${super<B>.hello()}"
// Strategy 3: Custom implementation
// return "D"
}
}
(3) Conflict Resolution Comparison
| Strategy | Syntax | Use Case |
|---|---|---|
| Choose parent A | super<A>.hello() |
A's implementation is more appropriate |
| Choose parent B | super<B>.hello() |
B's implementation is more appropriate |
| Combine both | super<A> + super<B> |
Need both behaviors |
| Custom implementation | Custom code | Neither is appropriate |
7. Inheritance Hierarchy Diagram
classDiagram
class Order {
+val id: String
+var status: String
+open fun summary(): String
+open fun calculateTotal(): Double
}
class BulkOrder {
+val minQuantity: Int
+override fun calculateTotal(): Double
}
class PriorityOrder {
+val priority: String
+override fun summary(): String
}
class Discountable {
<<interface>>
+fun discountRate(): Double
+fun applyDiscount(total: Double): Double
}
class Trackable {
<<interface>>
+val trackingCode: String?
+fun track(): String
}
Order <|-- BulkOrder
Order <|-- PriorityOrder
BulkOrder ..|> Discountable
PriorityOrder ..|> Trackable
PriorityOrder ..|> Discountable
8. Complete Example: OrderProcessor Inheritance Hierarchy
KOTLIN
// ============================================
// OrderProcessor - Inheritance Hierarchy
// Feature: Order / BulkOrder / PriorityOrder
// ============================================
interface Discountable {
fun discountRate(): Double
fun applyDiscount(total: Double): Double = total * (1.0 - discountRate())
}
interface Trackable {
val trackingCode: String?
fun track(): String = trackingCode?.let { "Tracking: $it" } ?: "Not shipped yet"
}
open class Order(val id: String, var status: String, val total: Double) {
init {
require(total >= 0) { "Total must be non-negative" }
}
open fun summary(): String = "Order $id | \$$total USD | $status"
open fun calculateFinal(): Double = total
}
class BulkOrder(
id: String,
status: String,
total: Double,
val minQuantity: Int
) : Order(id, status, total), Discountable {
override fun discountRate() = 0.10 // 10% bulk discount
override fun calculateFinal(): Double = applyDiscount(total)
override fun summary(): String =
"${super.summary()} | Bulk (min: $minQuantity) | Final: \$${calculateFinal()} USD"
}
class PriorityOrder(
id: String,
status: String,
total: Double,
val priority: String,
override val trackingCode: String?
) : Order(id, status, total), Discountable, Trackable {
override fun discountRate() = when (priority) {
"VIP" -> 0.20
"GOLD" -> 0.15
else -> 0.05
}
override fun calculateFinal(): Double = applyDiscount(total)
override fun summary(): String =
"${super.summary()} | Priority: $priority | ${track()} | Final: \$${calculateFinal()} USD"
}
fun main() {
val orders = listOf(
Order("ORD-001", "CONFIRMED", 299.99),
BulkOrder("ORD-002", "CONFIRMED", 5_000.00, 100),
PriorityOrder("ORD-003", "SHIPPED", 15_000.00, "VIP", "TRK-XYZ789"),
PriorityOrder("ORD-004", "PENDING", 2_500.00, "GOLD", null)
)
println("=== Order Summary ===")
orders.forEach { println(it.summary()) }
val totalSavings = orders
.filterIsInstance<Discountable>()
.sumOf { it.applyDiscount((it as Order).total) - (it as Order).calculateFinal() }
println("\nTotal savings from discounts: \$$totalSavings USD")
}
Output:
TEXT
=== Order Summary ===
Order ORD-001 | $299.99 USD | CONFIRMED
Order ORD-002 | $5000.0 USD | CONFIRMED | Bulk (min: 100) | Final: $4500.0 USD
Order ORD-003 | $15000.0 USD | SHIPPED | Priority: VIP | Tracking: TRK-XYZ789 | Final: $12000.0 USD
Order ORD-004 | $2500.0 USD | PENDING | Priority: GOLD | Not shipped yet | Final: $2125.0 USD
Total savings from discounts: $3475.0 USD
❓ FAQ
Q Why are Kotlin classes final by default?
A Effective Java recommends "design and document for inheritance, or prohibit it." Kotlin adopts this advice, defaulting to prohibit inheritance, using
open for explicit opening.Q Can interfaces have constructors?
A No. Interfaces have no constructors and cannot hold state. Use abstract classes for shared state.
Q Can a class resolve conflicts when implementing multiple interfaces with default methods?
A Yes. If two interfaces have default methods with the same name and signature, the subclass must override the method and use
super<InterfaceName> to choose an implementation.Q Can
override be omitted?A No. Kotlin mandates the
override keyword — omitting it causes a compile error. This is the key safety mechanism preventing accidental overrides.Q Do abstract class methods need
open?A No. Abstract methods in abstract classes are implicitly overridable (must be overridden). Non-abstract open methods still need the
open modifier.Q Does Kotlin support calling
super like Java?A Yes. Subclass constructors use
super(...) to call the parent constructor, methods use super.foo() to call the parent implementation, and interfaces use super<Interface>.foo().📖 Summary
- Kotlin classes are final by default; must be
openfor inheritance — closed by default, explicitly opened abstractclasses cannot be instantiated; abstract methods must be implemented by subclasses- Interfaces support default implementations and abstract properties; multiple implementation is allowed
overridemandatory annotation prevents accidental overrides — the compiler guards polymorphism safety- Multiple-inheritance conflicts are resolved with explicit
super<Interface>selection - Use interfaces for behavior contracts (stateless), abstract classes for shared state + behavior
📝 Exercises
- Beginner (⭐): Define an
Orderand aBulkOrderinheriting from it, overriding thesummarymethod. Hint:open class Order,class BulkOrder : Order() - Intermediate (⭐⭐): Create
DiscountableandTrackableinterfaces, havePriorityOrderimplement both, resolving a name conflict. Hint:super<Discountable> - Challenge (⭐⭐⭐): Design a complete order inheritance hierarchy:
BaseOrder→BulkOrder/PriorityOrder, each implementing different discount and shipping strategies. Hint: combine interfaces and abstract classes



