In Kotlin, two advanced features—delegation and object declarations—help you build cleaner, more modular, and maintainable code. Using the analogy of an airplane’s design and control systems, we’ll explore how delegation lets you share behavior, and how object declarations let you create single-instance managers.
Delegation is when one class “hands off” part of its responsibilities to another class (delegate). In Kotlin, this works with interfaces and properties using the by keyword.
Suppose you have interfaces for flying and moving. You can implement those behaviors once and reuse via delegation.
In this example, the Airplane class delegates its behaviors — flying and moving —
to two separate systems: Flyable and Movable.
Kotlin’s by keyword makes delegation easy and eliminates the need for redundant boilerplate code.
// Define interfaces representing behaviors
interface Flyable {
fun fly(): String
}
interface Movable {
fun move(): String
}
// JetFlySystem provides the actual implementation for flying
class JetFlySystem : Flyable {
override fun fly() = "Jet-style takeoff and high speed ascent"
}
// TurbofanPropulsion provides implementation for ground and air movement
class TurbofanPropulsion : Movable {
override fun move() = "Turbofan propulsion: taxi, accelerate, lift-off"
}
// Airplane delegates both Flyable and Movable interfaces to respective systems
class Airplane(
private val flySystem: Flyable, // delegate object for flying behavior
private val propulsion: Movable // delegate object for movement behavior
) : Flyable by flySystem, // Kotlin automatically forwards Flyable calls to flySystem
Movable by propulsion { // and Movable calls to propulsion
// No need to implement fly() or move() manually — compiler does it for you!
}
fun main() {
val plane = Airplane(JetFlySystem(), TurbofanPropulsion())
// These calls are automatically delegated to their respective systems
println(plane.fly()) // delegates to JetFlySystem.fly()
println(plane.move()) // delegates to TurbofanPropulsion.move()
}
Jet-style takeoff and high speed ascent
Turbofan propulsion: taxi, accelerate, lift-off
by keyword.Airplane composes behavior by combining different components (Flyable and Movable).fly() calls are redirected to flySystem.fly() and move() calls to propulsion.move().Flyable or Movable.by keyword removes the need to manually write boilerplate like override fun fly() = flySystem.fly().Output:
Jet-style takeoff and high speed ascent
Turbofan propulsion: taxi, accelerate, lift-off
Explanation:
Flyable and Movable by delegating to the provided systems.Sometimes you want to add extra logic around delegation (e.g., logging or safety checks). You can do it manually:
class AirplaneManual(
private val flySystem: Flyable,
private val propulsion: Movable
) : Flyable, Movable {
override fun fly(): String {
println("Pre-flight check: systems OK")
val result = flySystem.fly()
println("Post-flight log: flight completed")
return result
}
override fun move(): String {
println("Taxi check, wheels down")
return propulsion.move()
}
}
fun main() {
val plane = AirplaneManual(JetFlySystem(), TurbofanPropulsion())
println(plane.fly())
println(plane.move())
}
Output:
Pre-flight check: systems OK
Jet-style takeoff and high speed ascent
Post-flight log: flight completed
Taxi check, wheels down
Turbofan propulsion: taxi, accelerate, lift-off
Explanation:
by offers brevity, but manual delegation offers extension points.Delegation also applies to properties — you can delegate how a property’s getter or setter behaves. Imagine monitoring fuel level or landing gear status.
import kotlin.properties.Delegates
class AircraftSystems {
var fuelLevel: Int by Delegates.observable(100) { prop, old, new ->
println("Fuel level changed from \$old% to \$new%")
}
}
fun main() {
val systems = AircraftSystems()
systems.fuelLevel = 90
systems.fuelLevel = 75
}
Output:
Fuel level changed from 100% to 90%
Fuel level changed from 90% to 75%
Explanation:
fuelLevel uses a delegate which runs monitoring logic whenever its value changes.
In Kotlin, object declarations define a class and its single instance at once. This is perfect for single-point managers in aviation software.
object FlightControlCenter {
init {
println("FlightControlCenter initialized – monitoring all flights")
}
fun registerFlight(flightId: String) {
println("Flight \$flightId registered")
}
}
fun main() {
FlightControlCenter.registerFlight("A123")
FlightControlCenter.registerFlight("B456")
}
Output:
FlightControlCenter initialized – monitoring all flights
Flight A123 registered
Flight B456 registered
Explanation:
object keyword creates a thread-safe singleton named FlightControlCenter.init block executes only once—when the object is first accessed.Sometimes you need a one‐time handler—like a temporary flight-monitor object.
val tempHandler = object {
fun handleEmergency(flightId: String) {
println("Emergency handler for flight \$flightId activated")
}
}
fun main() {
tempHandler.handleEmergency("C789")
}
Use Case: Quick callbacks, event handlers in aviation simulation environments.
FlightControlCenter, Logger, MaintenanceScheduler implemented as singletons.by keyword simplify delegation?by and override specific ones manually for customization. :contentReference[oaicite:3]{index=3}