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
97: Writing Files in Kotlin
I was tinkering with a small RPG project last night and realized I had no way to actually save the player's progress. Everything lived in memory, so every time I restarted the app, my level 50 Paladin vanished. I figured it was time to get some data onto the disk.
The Quick and Dirty Way
When I first started, I didn't want to mess around with complex streams or buffers. I just wanted my stats in a file. Since Kotlin provides some great extension functions for java.io.File, I tried the most direct approach first.
val saveFile = File("player_save.txt")
saveFile.writeText("Level: 50\nClass: Paladin\nGold: 1200")
println("Game saved!")
This worked exactly as expected. I checked the folder, and there was my file. writeText is incredibly convenient because it handles opening the stream and closing it for you. But then I ran into a problem. I wanted to add a "Last Played" timestamp to the file without deleting the existing stats.
Wait, where did my data go?
I tried calling writeText again with the timestamp, but when I opened the file, the Level, Class, and Gold were gone. Only the timestamp remained. I realized writeText doesn't add to the file; it completely overwrites it. That's a dangerous habit to get into if you're building a log or a history file.
I needed a way to just "tack on" new information. I looked through the extension functions and found appendText. Let's see how that behaves:
val saveFile = File("player_save.txt")
saveFile.appendText("\nLast Played: 2023-10-27")
println("Timestamp added!")
Now the file contains both the original stats and the date. Much better. However, I started thinking about the "Game Log"—a feature where every single action the player takes is recorded. If I use appendText every time the player moves or attacks, I'm asking the OS to open the file, find the end, write a few bytes, and close the file... hundreds of times per minute. That's a recipe for a laggy game.
Scaling up for the long haul
For high-frequency writing, we need a buffer. Instead of hitting the disk for every single string, a buffer collects data in memory and writes it in one larger chunk. Kotlin gives us bufferedWriter() for this.
I tried implementing the game log this way, but I noticed something weird: if I just called bufferedWriter() and wrote to it, nothing appeared in the file until the program ended. I forgot that buffers need to be "flushed" or closed to ensure the data actually moves from RAM to the disk.
The cleanest way to handle this is using .use { }. This ensures the writer is closed automatically, even if the code throws an exception. Here is how I settled on doing the logging:
val logFile = File("game_log.txt")
logFile.bufferedWriter().use { out ->
out.write("Player entered the Dark Forest\n")
out.write("Player encountered a Goblin\n")
out.write("Player dealt 15 damage\n")
}
I've found that writeText is perfect for config files, appendText is great for occasional updates, and bufferedWriter is the only way to go when you're dealing with loops or large amounts of data. If you don't use that .use block, you're basically gambling with your data.
📋 Practical Task
Build a Player Inventory Persistence System
Create a program that simulates saving a player's inventory to a file. You should start with a list of items (e.g., "Iron Sword", "Health Potion", "Ancient Map").
- Create a
Fileobject namedinventory.txt. - Use a
bufferedWriterwithin a.useblock to iterate through your list of items and write each one on a new line. - After writing the inventory, use
appendTextto add a single line at the bottom of the file indicating the total number of items saved. - Verify that the file contains both the individual items and the final count.
There are no comments for now.