Have you ever stumbled upon the challenge of copying complex, nested Java/ Kotlin data structures without losing your mind or cluttering your codebase with tedious boilerplate? You’re not alone.
Imagine you’re building an enterprise-grade Kotlin application, dealing with objects deeply nested within multiple layers — such as users, addresses, cities, and countries. Copying these deeply nested objects without accidentally introducing shared references is tricky. But here’s the good news: There’s a simple, elegant, and powerful solution — MapStruct.
The Challenge: Why deep copying matters
Let’s face reality: shallow copies of complex nested objects are a ticking time bomb. They look innocent until changes in one object silently ripple through copies, causing bugs that are notoriously difficult to detect and fix.
Here’s an example of a deeply nested Kotlin object:
data class User(
val userId: Long,
val fullName: String,
val address: Address,
val manager: User? = null,
)
data class Address(
val streetAddress: String,
val city: City,
)
data class City(
val cityName: String,
val country: Country,
)
data class Country(
val countryName: String,
)
Imagine having to manually deep copy instances of User
with nested objects—tedious, error-prone, and frankly, exhausting.
Why traditional solutions fall short
Popular methods like manually using copy()
with custom parameters, implementing the Cloneable
interface, or serializing objects through JSON conversions, all come with significant drawbacks:
Verbose code: Manual copying or cloning quickly bloats your codebase.
Error-prone: Every manual step increases the risk of mistakes.
Performance overhead: JSON serialization/deserialization introduces unnecessary performance penalties.
Enter MapStruct: your new best friend
MapStruct is a powerful annotation processor that generates deep-copy code for you. With minimal setup, you get clean, efficient, and error-free copies of your Kotlin objects.
Here’s how simple your deep copy logic can become with MapStruct:
@Mapper(mappingControl = DeepClone::class)
interface DeepCopyMapper {
fun deepCopyUser(sourceUser: User): User
}
That’s it! MapStruct generates all the complex nested copying logic automatically.
Project setup
Use build.gradle.kts below
plugins {
kotlin("jvm") version "2.1.10"
kotlin("kapt") version "2.1.20"
application
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib"))
implementation("org.mapstruct:mapstruct:1.5.3.Final")
kapt("org.mapstruct:mapstruct-processor:1.5.3.Final")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(21)
}
See it in action: Cloning complex objects
Here’s how easy it becomes to use this mapper:
fun main() {
val deepCopyMapper = Mappers.getMapper(DeepCopyMapper::class.java)
val subordinate = User(
userId = 1L,
fullName = "Bob Employee",
address = Address(
streetAddress = "100 Market Street",
city = City(
cityName = "San Francisco",
country = Country("USA"),
),
),
manager = User(
userId = 2L,
fullName = "Alice Powder",
address = Address(
streetAddress = "200 Super Street",
city = City(
cityName = "Wrocław",
country = Country("Poland"),
),
),
manager = null
)
)
val copiedSubordinate = deepCopyMapper.deepCopyUser(subordinate)
// These print exactly the same objects
println("Original: $subordinate")
println("Copied: $copiedSubordinate")
}
Behind the Scenes: What MapStruct Does
MapStruct generates a clear, readable Java implementation under the hood, deeply cloning each nested property safely and efficiently. You get:
Zero boilerplate: Save countless hours of manual coding.
Safety guaranteed: No accidental shared references.
Maintainable code: Easy to read and extend.
Why you should embrace MapStruct today
Adopting MapStruct for deep copying is a game-changer for Java / Kotlin developers:
Rapid Development: Spend less time on mundane tasks.
Reduced Bugs: Prevent subtle, hard-to-find errors.
Future Proof: Adapt and scale your application easily.
Ready to Start?
Stop wasting valuable development time on manual deep copies. Integrate MapStruct into your Kotlin project today and experience the magic for yourself.
Curious about how the generated mapper implementation looks under the hood? You can check out the full code, including the auto-generated DeepCopyMapperImpl
, here:
👉 https://gist.github.com/Sedose/f821be1c7d187be1c40a685d0cd0fd88
Thanks for reading and happy coding! 🚀