This article was originally written on medium and migrated to here.
To make your code more flexible and maintainable, it’s essential to understand composition and inheritance. This article features two simple examples With Android Activities to illustrate these design patterns, and explains the by keyword in Kotlin. Code source available in this GitHub repository.

Understanding Composition and Inheritance
Composition and inheritance are two fundamental design principles in object-oriented programming:
- Inheritance allows a class to inherit properties and methods from another class, promoting code reuse.
- Composition involves building classes using other classes, promoting more flexible and modular code structures.
Let’s delve into examples to see how these principles are applied in Kotlin.
Example with Inheritance
In the inheritance-based approach, we have a MainActivityInheritance class that extends a BaseActivity. The BaseActivity contains shared functionality that is inherited by MainActivityInheritance.
class MainActivityInheritance : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startView()
}
}
open class BaseActivity : AppCompatActivity() {
fun startView() {
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startView()
}
}
Pros of Inheritance:
- Simple and straightforward.
- Encourages code reuse through shared base class functionality.
Cons of Inheritance:
- Tight coupling between the base class and derived class.
- BaseActivity tells nothing about what this class do. We’ll need to open this class to understand better.
- Less flexibility and harder to modify behavior without affecting all derived classes.
Example with Composition
In the composition-based approach, we use Kotlin’s by keyword to delegate the implementation of an interface to another class. The MainActivityComposition class implements the ViewStarter interface by delegating to ViewStarterImpl.
class MainActivityComposition : AppCompatActivity(), ViewStarter by ViewStarterImpl {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
startView(findViewById(R.id.main))
}
}
interface ViewStarter {
fun startView(view: View)
}
object ViewStarterImpl : ViewStarter {
override fun startView(view: View) {
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}
Pros of Composition:
- Greater flexibility and modularity, we can just create another activity and implement the same interface and the same ViewStarterImpl.
- ViewStarter interface make clear for us that this is starting a view. Is more readable.
- Looser coupling between classes.
- Easier to modify or extend functionality.
Cons of Composition:
- Slightly more complex to set up and understand initially.
- A little more boilerplate code to delegate behaviors.
Why Choose Composition?
The choice between composition and inheritance largely depends on your specific use case. However, composition often provides greater flexibility and promotes a more modular architecture, especially in complex applications.
Understanding the Kotlin “by” Keyword
The by keyword, especially in the context of ViewStarter by ViewStarterImpl, can be a bit tricky at first. To make it easier, let’s imagine I’m the MainActivityComposition object saying:
I will implement the
ViewStarterinterface, but I don’t want to override the interface methods! Instead, I’ll ask another object, calledViewStarterImpl, to implement it for me.
As The activity says, Using composition, MainActivityComposition can access all methods from the ViewStarter interface without worrying about the implementation details. This delegation allows for cleaner and more maintainable code.
Conclusion
In this article, we’ve explored the differences between composition and inheritance in Kotlin using practical examples, using the by keyword.
Source code here.