I do not want a debate about data class’s auto-generated copy() method and what someone should or should not do with data classes.
Let’s just assume that someone wants to write a class whose identity is determined only by the value of its stored properties, but they want a non-public constructor (which is undermined by data class’s copy()).
It seems to me that we have approximately three options:
Regular old class with equals(), hashCode(), and toString() implemented “by hand”.
Using @jvmRecord annotation on a regular class, which will generate equals(), hashCode(), and toString(), componentN(), but not a copy().
Using this compiler plugin library: https://github.com/AhmedMourad0/no-copy
Eventually, Kotlin’s native value classes are supposed to be expanded to support more than a single field. But that’s dependent on project Valhalla landing on the JVM, so I don’t think we should hold our breath waiting for that.
The flaw with the “by hand” approach is the same old story: if you change your class you have to be very careful to remember to update all the methods. It’s also more noise for a reader- they don’t know if your equals() implementation is just boilerplate or if you’re actually doing something interesting, so they’re forced to study it carefully.
The flaw with the @jvmRecord annotation is that all Record classes inherit from a base Java class, which means that your class can’t inherit from any other class. There may also be other issues/papercuts I’m unaware of (compile time? generated method name issues? runtime performance issues?).
The flaw with the no-copy approach is that you have to globally disable IDE warnings to avoid false-positives (although, it looks like that might be fixed now?), and just that it’s a compiler plugin- so it might (“probably will”, if you’re cynical like me) break on Kotlin version bumps. I also don’t know if it will affect compile times.
It seems like the no-copy plugin is probably the best bet today. Is there any option I’m missing?