Given the Kotlin data class:
data class Entity(
val uuidId: UUID?,
val stringId: String?,
val stuff: String
) {
constructor(id: String, stuff: String) : this(
uuidId = try { UUID.fromString(id) } catch(_:Throwable) { null },
stringId = try { UUID.fromString(id); null } catch(_:Throwable) { id },
stuff = stuff
)
}
Can I avoid doing UUID.fromString(id)
twice?
Given the Kotlin data class:
data class Entity(
val uuidId: UUID?,
val stringId: String?,
val stuff: String
) {
constructor(id: String, stuff: String) : this(
uuidId = try { UUID.fromString(id) } catch(_:Throwable) { null },
stringId = try { UUID.fromString(id); null } catch(_:Throwable) { id },
stuff = stuff
)
}
Can I avoid doing UUID.fromString(id)
twice?
- Unless your Entity class reflects a DB row, for example, consider redesigning it as an interface, and implement it with a class that holds a String and one that holds a UUID. – Klitos Kyriacou Commented Mar 13 at 9:22
- @KlitosKyriacou. It is an entity. – Kurt Commented Mar 13 at 13:21
2 Answers
Reset to default 3Declaring a local variable in a secondary constructor might have helped, but you cannot declare local variables inside a secondary constructor.
There is a discussion on a similar topic in the Kotlin forum Secondary constructor initiation logic on data class
I would suggest to add a companion object with an invoke function:
data class Entity(
val uuidId: UUID?,
val stringId: String?,
val stuff: String
) {
companion object {
operator fun invoke(id: String, stuff: String): Entity {
val uuid = try { UUID.fromString(id) } catch (_: Throwable) { null }
val stringId = if (uuid != null) null else id
return Entity(uuid, stringId, stuff)
}
}
}
@Test
fun `create entity using invoke function`() {
val entityWithUuid = Entity("123e4567-e89b-12d3-a456-426614174000", "staff_1")
val entityWithStringId = Entity("my_id", "staff_2")
// Entity(uuidId=123e4567-e89b-12d3-a456-426614174000, stringId=null, stuff=staff_1)
println(entityWithUuid)
// Entity(uuidId=null, stringId=my_id, stuff=staff_2)
println(entityWithStringId)
}
Another approach is to define an an explicit factory method inside companion object
companion object {
fun create(id: String, stuff: String): Entity {
// same body as in invoke
}
}
And then create an object
val entity = Entity.create("123e4567-e89b-12d3-a456-426614174000", "staff_1")
Here is an invoke function with minor changes, maybe you'll find them handy
operator fun invoke(id: String, stuff: String): Entity = try {
Entity(UUID.fromString(id), null, stuff)
} catch (_: Throwable) {
Entity(null, id, stuff)
}
I would have it go through another secondary constructor - one that takes a Pair<UUID?, String?>
.
private constructor(idPair: Pair<UUID?, String?>, stuff: String): this(
uuidId = idPair.first,
stringId = idPair.second,
stuff = stuff,
)
Then you can use various scope functions in the other secondary constructor to construct the Pair
.
constructor(id: String, stuff: String) : this(
idPair = id.runCatching { UUID.fromString(this) }.let { result ->
Pair(result.getOrNull(), id.takeIf { result.isFailure })
},
stuff = stuff
)