-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
204: Common Kotlin Interview Questions on Extension Functions
I remember interviewing a senior developer a few years back for a role on my team. He was brilliant at architecture, but when I asked him how extension functions actually work under the hood, he confidently told me that they "modify the class bytecode to inject new methods at runtime." I had to stop him right there. That one misconception is a massive red flag in a Kotlin interview because it reveals a fundamental misunderstanding: extension functions don't actually change the class they extend. If you tell an interviewer that extensions are "adding" methods to a class, you're likely to get hit with a follow-up question that trips you up.
The Static Resolution Trap
The most common "gotcha" question revolves around static resolution. Because extension functions are essentially just static methods that take the receiver object as an argument, they don't participate in polymorphism. I've seen countless candidates lose points here because they expect an extension function to behave like an overridden method.
class Shape
class Circle : Shape()
fun Shape.getName() = "I am a shape"
fun Circle.getName() = "I am a circle"
fun printName(s: Shape) {
println(s.getName())
}
// If you pass a Circle into printName, it prints "I am a shape"
// NOT "I am a circle"
In the example above, printName expects a Shape. Since the resolution happens at compile time based on the declared type (not the runtime type), Kotlin calls the Shape extension. If this were a real member function override, you'd get "I am a circle." When an interviewer asks, "Are extension functions virtual?", your answer should be a firm "No."
When Members Clash with Extensions
Another favorite interview topic is shadowing. What happens when you write an extension function that has the exact same signature as a member function already existing in the class? The member function always wins. Always. No exceptions.
I once worked on a project where a teammate tried to "override" a third-party library's isValid() method by writing an extension function with the same name. He spent an entire afternoon wondering why his logic wasn't executing, only to realize the original library method was still being called. If you find yourself in an interview and they ask how to resolve this conflict, the answer is that you can't "override" a member with an extension; you'd have to name your extension something else or wrap the class in a decorator.
Playing with Nullability
Finally, you'll likely be asked about nullable receivers. This is where extension functions actually become incredibly powerful. You can define an extension on a nullable type (like String?), which allows you to handle the null check inside the function rather than every time you call it.
fun String?.isNotNullOrBlank(): Boolean {
return this != null && this.isNotBlank()
}
val name: String? = null
if (name.isNotNullOrBlank()) {
// This is clean and avoids the "name?.let { ... }" nesting
}
I personally love this pattern because it cleans up the call site significantly. In an interview, pointing this out shows that you aren't just using the language syntax, but you're thinking about how to reduce boilerplate and improve readability for the rest of your team.
📋 Practical Task
Building a Null-Safe Formatting Library for User Profiles
Imagine you are working on a social media app. You have a UserProfile class where several fields (like bio and location) are nullable. Instead of cluttering your UI code with if (user.bio != null) checks, you need to create a set of extension functions to handle the formatting.
Requirements:
- Create a
UserProfiledata class with properties:username: String,bio: String?, andlocation: String?. - Write an extension function
String?.formatBio(): Stringthat returns the bio text if it exists, or returns "This user hasn't written a bio yet" if it is null or blank. - Write an extension function
String?.formatLocation(): Stringthat returns the location text if it exists, or returns "Location hidden" if it is null. - In your
mainfunction, create oneUserProfilewith all fields filled and one withbioandlocationset tonull. Call your extension functions on both profiles and print the results to the console.
There are no comments for now.