How about this solution?
Usage example one:
class ProductOne(
val id: Int,
val name: String,
val manufacturer: String
) {
override fun equals(other: Any?): Boolean = equalsHelper(other, ProductOne::name, ProductOne::manufacturer)
override fun hashCode(): Int = hashCodeHelper(ProductOne::name, ProductOne::manufacturer)
override fun toString(): String = toStringHelper(ProductOne::name, ProductOne::manufacturer)
}
Usage example two:
class ProductTwo(
val id: Int,
val name: String,
val manufacturer: String
) {
companion object {
private val PROPS = arrayOf(ProductTwo::name, ProductTwo::manufacturer)
}
override fun equals(other: Any?): Boolean = equalsHelper(other, *PROPS)
override fun hashCode(): Int = hashCodeHelper(*PROPS)
override fun toString(): String = toStringHelper(*PROPS)
}
Helpers:
fun <T : Any> T.equalsHelper(other: Any?, vararg props: KProperty1<T, *>): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
for (prop in props) {
@Suppress("UNCHECKED_CAST")
if (prop.get(this) != prop.get(other as T)) return false
}
return true
}
fun <T : Any> T.hashCodeHelper(vararg props: KProperty1<T, *>): Int {
return props.fold(31) { acc, prop -> acc * prop.get(this).hashCode() }
}
fun <T : Any> T.toStringHelper(vararg props: KProperty1<T, *>): String {
return buildString {
append(this::class.simpleName)
append("(")
props.joinToString() { prop ->
"${prop.name} = ${prop.get(this@toStringHelper).toString()}"
}
append(")")
}
}