Kotlin
Completed
-
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
22: Object Declarations and Companion Objects
In Java, if you wanted a Singleton, you had to write a bunch of boilerplate: a private constructor, a static instance variable, and a synchronized getter. It was tedious and easy to mess up. In Kotlin, we have the object keyword, which handles all of that in one go.
Creating a global settings hub
Let's imagine we're building a simple game. We need a place to store global configuration—things like the screen resolution or the volume level—that every other part of the game can access without us having to pass a settings object through twenty different constructors.
object GameSettings {
var volume: Float = 0.8f
var resolution: String = "1920x1080"
var isMuted: Boolean = false
fun resetToDefaults() {
volume = 0.8f
resolution = "1920x1080"
isMuted = false
}
}
By using object instead of class, I've told Kotlin that GameSettings is a Singleton. There is exactly one instance of this object in the entire application, and it's created lazily the first time it's accessed. I can just call GameSettings.volume = 0.5f from anywhere in my code. It's clean, and it's thread-safe by default.
Oops, I forgot how Singletons work
I'll be honest: even after years of doing this, when I'm typing fast, I sometimes slip back into "class mode." I just tried to do this in my IDE:
val mySettings = GameSettings() // Error: 'object' GameSettings cannot be instantiated
The compiler immediately yelled at me. You can't call a constructor on an object because there is no constructor for you to call—the language handles the instantiation behind the scenes. If I find myself wanting to create multiple instances of something, I need to change it back to a class. But for global config? The object declaration is exactly what we want.
Tying logic to the Player class
Now, what if I want some functionality that belongs to a class, but doesn't actually need a specific instance of that class to work? In Java, you'd use static. In Kotlin, we use a companion object.
Let's say we have a Player class. We want a way to create a "Default" player without manually filling in every property every time. I'll put a factory method inside a companion object:
class Player(val name: String, val level: Int) {
companion object {
const val MAX_LEVEL = 100
fun createGuestPlayer(): Player {
return Player("Guest_${(1..1000).random()}", 1)
}
}
}
The companion object is essentially an object tied to the Player class. Notice that I used const val for MAX_LEVEL. Because it's inside a companion object and marked as const, it's treated as a true compile-time constant, which is more efficient than a regular read-only property.
Now, when I'm in my main game loop, I can do this:
val p1 = Player("Hero", 10)
val p2 = Player.createGuestPlayer()
println("Max level is ${Player.MAX_LEVEL}")
I'm calling createGuestPlayer() and accessing MAX_LEVEL directly on the class name Player, not on an instance of a player. This keeps the namespace organized—anything related to the idea of a Player lives in the companion object, while anything related to a specific Player lives in the class body.
📋 Practical Task
Building a Database Connection Manager and User Factory
You need to implement a small system for managing a mock database connection and user creation. Complete the following requirements:
- Create an
objectcalledDbConnection. It should have a propertyconnectionString(set to "jdbc:mysql://localhost:3306/mydb") and a functionconnect()that prints "Connecting to [connectionString]...". - Create a
Userclass with propertiesusernameandrole(both Strings). - Inside the
Userclass, add acompanion object. - In that companion object, create a function
createAdmin(name: String)that returns aUserinstance with the provided name and the role "Administrator". - In your
mainfunction:- Call the
connect()method using theDbConnectionsingleton. - Create a new admin user using the
User.createAdmin()factory method and print their role to the console.
- Call the
There are no comments for now.