CLOVIS
07/10/2023, 3:43 PMclass Example internal constructor(
val foo: Int,
val bar: String,
val baz: Foo,
)
It's very important for me that:
• ✅ I can add new fields in a backwards-compatible manner (as you can see, the constructor is internal
, so adding new fields is not a binary compatibility issue when constructing the object)
• ✅ Low boilerplate: it's used in a lot of places!
• ❌ They can be compared using `equals`/`hashCode`.
The first solution that comes to mind would be using data class
, but then changing the order of fields is binary-incompatible due to componentN
, and adding a field is binary-incompatible due to copy
.
Writing equals
and hashCode
manually is a lot of boilerplate, and susceptible to mistakes if fields are added in the future without updating them. It's also harder to review.
Is there some way to have data class
-like `equals`/`hashCode` (/ optionally toString
), without copy
nor componentN
, to ensure binary-compatibility when adding fields/changing their order later?Chris Lee
07/10/2023, 3:50 PMclass Example internal constructor(
val foo: Int,
val bar: String,
val baz: Foo,
) {
private val impl = Impl(foo = foo, bar - bar, baz = baz)
override fun equals(other: Any?): Boolean {
if( other !is Example) {
return false
}
return impl.equals(other.impl)
}
override fun hashCode(): Int {
return impl.hashCode()
}
override fun toString(): String {
return impl.toString()
}
private data class Impl(val foo : Int, val bar : String, val baz : Foo)
}
CLOVIS
07/10/2023, 3:51 PMChris Lee
07/10/2023, 3:52 PMdata class
would work nicely. What are the issues with binary compatibility preventing that?CLOVIS
07/10/2023, 3:54 PMCLOVIS
07/10/2023, 3:56 PMinterface Example {
val foo: Int,
val bar: String,
val baz: Foo,
}
private data class ExampleImpl(
override val foo: Int,
override val bar: String,
override val baz: Foo,
) : Example
Still requires to duplicate everything, though.Chris Lee
07/10/2023, 3:57 PMChris Lee
07/10/2023, 4:00 PMJeff Lockhart
07/10/2023, 4:53 PMephemient
07/10/2023, 6:43 PM