Kotlin equals and hash code generator - kotlin

I am aware that in Kotlin classes will have an equals and hashcode created automatically as follows:
data class CSVColumn(private val index: Int, val value: String) {
}
My question is, is there a way to have the implementation just use one of these properties (such as index) without writing the code yourself. What was otherwise a very succinct class now looks like this:
data class CSVColumn(private val index: Int, val value: String) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (javaClass != other?.javaClass) {
return false
}
other as CSVColumn
if (index != other.index) {
return false
}
return true
}
override fun hashCode(): Int {
return index
}
}
In Java with Lombok, I can do something like:
#Value
#EqualsAndHasCode(of="index")
public class CsvColumn {
private final int index;
private final String value;
}
Would be cool if there were a way to tell Kotlin something similar.

From the Data Classes documentation you get:
Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body
So you have to implement equals() and hashCode() manually or with the help of a Kotlin Compiler Plugin.

You can't do something like this for data classes, they always generate equals and hashCode the same way, there's no way to provide them such hints or options.
However, they only include properties that are in the primary constructor, so you could do this for them to only include index:
data class CSVColumn(private val index: Int, value: String) {
val value: String = value
}
... except you can't have parameters in the primary constructor that aren't properties when you're using data classes.
So you'd have to somehow introduce a secondary constructor that takes two parameters, like this:
class CSVColumn private constructor(private val index: Int) {
var value: String = ""
constructor(index: Int, value: String) : this(index) {
this.value = value
}
}
... but now your value property has to be a var for the secondary constructor to be able to set its value.
All this to say that it's probably not worth trying to work around it. If you need an non-default implementation for equals and hashCode, data classes can't help you, and you'll need to implement and maintain them manually.
Edit: as #tynn pointed out, a private setter could be a solution so that your value isn't mutable from outside the class:
class CSVColumn private constructor(private val index: Int) {
var value: String = ""
private set
constructor(index: Int, value: String) : this(index) {
this.value = value
}
}

I wrote a little utility called "stem", which allows you to select which properties to consider for equality and hashing. The resulting code is as small as it can get with manual equals()/hashCode() implementation:
class CSVColumn(private val index: Int, val value: String) {
private val stem = Stem(this, { index })
override fun equals(other: Any?) = stem.eq(other)
override fun hashCode() = stem.hc()
}
You can see its implementation here.

I guess that we have to write equals()/hashCode() manually for now.
https://discuss.kotlinlang.org/t/automatically-generate-equals-hashcode-methods/3779
It is not supported and is planning to be, IMHO.

I guess that we have to write equals()/hashCode() manually for now. https://discuss.kotlinlang.org/t/automatically-generate-equals-hashcode-methods/3779
It is not supported and is planning to be, IMHO.
Below are some reference which may be helpful.
https://discuss.kotlinlang.org/t/how-does-kotlin-implement-equals-and-hashcode/940
https://kotlinlang.org/docs/reference/data-classes.html
https://medium.com/#appmattus/effective-kotlin-item-11-always-override-hashcode-when-you-override-equals-608a090aeaed

See the following performance optimized way (with the use of value classes and inlining) of implementing a generic equals/hashcode for any Kotlin class:
#file:Suppress("EXPERIMENTAL_FEATURE_WARNING")
package org.beatkit.common
import kotlin.jvm.JvmInline
#Suppress("NOTHING_TO_INLINE")
#JvmInline
value class HashCode(val value: Int = 0) {
inline fun combineHash(hash: Int): HashCode = HashCode(31 * value + hash)
inline fun combine(obj: Any?): HashCode = combineHash(obj.hashCode())
}
#Suppress("NOTHING_TO_INLINE")
#JvmInline
value class Equals(val value: Boolean = true) {
inline fun combineEquals(equalsImpl: () -> Boolean): Equals = if (!value) this else Equals(equalsImpl())
inline fun <A : Any> combine(lhs: A?, rhs: A?): Equals = combineEquals { lhs == rhs }
}
#Suppress("NOTHING_TO_INLINE")
object Objects {
inline fun hashCode(builder: HashCode.() -> HashCode): Int = builder(HashCode()).value
inline fun hashCode(vararg objects: Any?): Int = hashCode {
var hash = this
objects.forEach {
hash = hash.combine(it)
}
hash
}
inline fun hashCode(vararg hashes: Int): Int = hashCode {
var hash = this
hashes.forEach {
hash = hash.combineHash(it)
}
hash
}
inline fun <T : Any> equals(
lhs: T,
rhs: Any?,
allowSubclasses: Boolean = false,
builder: Equals.(T, T) -> Equals
): Boolean {
if (rhs == null) return false
if (lhs === rhs) return true
if (allowSubclasses) {
if (!lhs::class.isInstance(rhs)) return false
} else {
if (lhs::class != rhs::class) return false
}
#Suppress("unchecked_cast")
return builder(Equals(), lhs, rhs as T).value
}
}
This allows you to write a equals/hashcode implementation as follows:
data class Foo(val title: String, val bytes: ByteArray, val ignore: Long) {
override fun equals(other: Any?): Boolean {
return Objects.equals(this, other) { lhs, rhs ->
combine(lhs.title, rhs.title)
.combineEquals { lhs.bytes contentEquals rhs.bytes }
}
}
override fun hashCode(): Int {
return Objects.hashCode(title, bytes.contentHashCode())
}
}

Related

Implementing observable properties that can also serialize in Kotlin

I'm trying to build a class where certain values are Observable but also Serializable.
This obviously works and the serialization works, but it's very boilerplate-heavy having to add a setter for every single field and manually having to call change(...) inside each setter:
interface Observable {
fun change(message: String) {
println("changing $message")
}
}
#Serializable
class BlahVO : Observable {
var value2: String = ""
set(value) {
field = value
change("value2")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value2 = "test2" })
correctly outputs
changing value2
{"value2":"test2"}
I've tried introducing Delegates:
interface Observable {
fun change(message: String) {
println("changing $message")
}
#Suppress("ClassName")
class default<T>(defaultValue: T) {
private var value: T = defaultValue
operator fun getValue(observable: Observable, property: KProperty<*>): T {
return value
}
operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
this.value = value
observable.change(property.name)
}
}
}
#Serializable
class BlahVO : Observable {
var value1: String by Observable.default("value1")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
println(BlahVO().apply { value1 = "test1" }) correctly triggers change detection, but it doesn't serialize:
changing value1
{}
If I go from Observable to ReadWriteProperty,
interface Observable {
fun change(message: String) {
println("changing $message")
}
fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
return OP(defaultValue, this)
}
class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
super.setValue(thisRef, property, value)
observable.change("blah!")
}
}
}
#Serializable
class BlahVO : Observable {
var value3: String by this.look("value3")
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
the result is the same:
changing blah!
{}
Similarly for Delegates.vetoable
var value4: String by Delegates.vetoable("value4", {
property: KProperty<*>, oldstring: String, newString: String ->
this.change(property.name)
true
})
outputs:
changing value4
{}
Delegates just doesn't seem to work with Kotlin Serialization
What other options are there to observe a property's changes without breaking its serialization that will also work on other platforms (KotlinJS, KotlinJVM, Android, ...)?
Serialization and Deserialization of Kotlin Delegates is not supported by kotlinx.serialization as of now.
There is an open issue #1578 on GitHub regarding this feature.
According to the issue you can create an intermediate data-transfer object, which gets serialized instead of the original object. Also you could write a custom serializer to support the serialization of Kotlin Delegates, which seems to be even more boilerplate, then writing custom getters and setters, as proposed in the question.
Data Transfer Object
By mapping your original object to a simple data transfer object without delegates, you can utilize the default serialization mechanisms.
This also has the nice side effect to cleanse your data model classes from framework specific annotations, such as #Serializable.
class DataModel {
var observedProperty: String by Delegates.observable("initial") { property, before, after ->
println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
}
fun toJson(): String {
return Json.encodeToString(serializer(), this.toDto())
}
}
fun DataModel.toDto() = DataTransferObject(observedProperty)
#Serializable
class DataTransferObject(val observedProperty: String)
fun main() {
val data = DataModel()
println(data.toJson())
data.observedProperty = "changed"
println(data.toJson())
}
This yields the following result:
{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}
Custom data type
If changing the data type is an option, you could write a wrapping class which gets (de)serialized transparently. Something along the lines of the following might work.
#Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
fun toJson(): String {
return Json.encodeToString(serializer(), this)
}
}
fun main() {
val monitoredString = obs("obsDefault") { before, after ->
println("""I changed from "$before" to "$after"!""")
}
val data = ClassWithMonitoredString(monitoredString)
println(data.toJson())
data.monitoredProperty.value = "obsChanged"
println(data.toJson())
}
Which yields the following result:
{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}
You however lose information about which property changed, as you don't have easy access to the field name. Also you have to change your data structures, as mentioned above and might not be desirable or even possible. In addition, this work only for Strings for now, even though one might make it more generic though.
Also, this requires a lot of boilerplate to start with. On the call site however, you just have to wrap the actual value in an call to obs.
I used the following boilerplate to get it to work.
typealias OnChange = (before: String, after: String) -> Unit
#Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
var value: String = initialValue
set(value) {
onChange?.invoke(field, value)
field = value
}
}
fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)
object MonitoredStringSerializer : KSerializer<MonitoredString> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: MonitoredString) {
encoder.encodeString(value.value)
}
override fun deserialize(decoder: Decoder): MonitoredString {
return MonitoredString(decoder.decodeString(), null)
}
}

Kotlin - TypeReference<T> Cannot obtain Class<*> For Type Arguments

I've created a Kotlin equivalent of TypeReference<T> like so:
abstract class TypeReference<T> : Comparable<T> {
val type: Type get() = getGenericType()
val arguments: List<Type> get() = getTypeArguments()
final override fun compareTo(other: T): Int {
return 0
}
private fun getGenericType(): Type {
val superClass = javaClass.genericSuperclass
check(superClass !is Class<*>) {
"TypeReference constructed without actual type information."
}
return (superClass as ParameterizedType).actualTypeArguments[0]
}
private fun getTypeArguments(): List<Type> {
val type = getGenericType()
return if (type is ParameterizedType) {
type.actualTypeArguments.toList()
} else emptyList()
}
}
In order to obtain Class<*> of the generic type and its arguments, I've also created the following extension function (and this is where I believe the problem lies, since this is where the stack trace fails).
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
else -> Class.forName(typeName)
}
I'm unit testing this like so:
#Test
fun `TypeReference should correctly identify the List of BigDecimal type`() {
// Arrange
val expected = List::class.java
val expectedParameter1 = BigDecimal::class.java
val typeReference = object : TypeReference<List<BigDecimal>>() {}
// Act
val actual = typeReference.type.toClass()
val actualParameter1 = typeReference.arguments[0].toClass()
// Assert
assertEquals(expected, actual)
assertEquals(expectedParameter1, actualParameter1)
}
The problem I think, lies in the extension function else -> Class.forName(typeName) as it throws:
java.lang.ClassNotFoundException: ? extends java.math.BigDecimal
Is there a better way to obtain the Class<*> of a Type, even when they're generic type parameters?
You need to add is WildcardType -> ... branch to your when-expression to handle types like ? extends java.math.BigDecimal (Kotlin equivalent is out java.math.BigDecimal), ?(Kotlin equivalent is *), ? super Integer(Kotlin equivalent is in java.math.Integer):
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
is WildcardType -> upperBounds.singleOrNull()?.toClass() ?: Any::class.java
else -> Class.forName(typeName)
}
Note that in this implementation single upper bound types will be resolved as its upper bound, but all other wildcard types (including multiple upper bounds types) will be resolved as Class<Object>
https://github.com/pluses/ktypes
val typeReference = object : TypeReference<List<BigDecimal>>() {}
val superType = typeReference::class.createType().findSuperType(TypeReference::class)!!
println(superType.arguments.first())// List<java.math.BigDecimal>
println(superType.arguments.first().type?.arguments?.first())// java.math.BigDecimal

Getting list of enums from bitmask in Kotlin

I have an enum class SettingsVisibility inside an database entity, which I am converting to a bitmask to store in the database. I am trying to reverse the conversion to bitmask, and get a list of enums as a return value. So if I have an enum with values ONE(1), TWO(2), FOUR(4), then it'll store as Enum(7). I want to take 7 and convert it to {ONE, TWO, FOUR}.
My code is below. I have the SettingsVisibility enum with integer values which are stored in the DB. When I try to retrieve from the database, Objectbox will use the given PropertyConvertor to marshall/unmarshall the data. When I want to convertToEntityProperty, it should return a list of just the saved enums, but at the moment it returns a list of all the enums. I can pass a databaseValue of 12 and it will return all enums instead of just 2 (LOCATION AND PAYMENTS).
I think the issue is the usage of enumClass.enumConstants because it gets all the values, but then the filter doesn't work on this, so I am stuck.
#Entity
data class Settings(
#Id override var id: Long = 0,
#Convert(converter = DocumentVisibilityConverter::class, dbType = Int::class)
val showItems: List<SettingsVisibility>
) : Identifiable<Long> {
lateinit var organisation: ToOne<Organisation>
constructor() : this(
showItems = emptyList(),
)
enum class SettingsVisibility(override val bit: Int) : Flags {
USERS(1),
FINANCE(2),
LOCATION(4),
PAYMENTS(8),
MESSAGES(16),
ERRORS(32),
CANCELLATIONS(64)
}
internal class DocumentVisibilityConverter
: BoxConverters.EnumFlagConverter<SettingsVisibility>(SettingsVisibility::class.java)
}
So for example, if I store the first 3, the database value will be 7 (1+2+4).
The database is ObjectBox and here are the property converters:
abstract class EnumFlagConverter<E>(private val enumClass: Class<E>) : PropertyConverter<List<E>, Int> where E : Enum<E>, E : Flags {
override fun convertToDatabaseValue(entityProperty: List<E>?): Int? {
return entityProperty?.toBitMask()?.value
}
override fun convertToEntityProperty(databaseValue: Int?): List<E>? {
return databaseValue?.let(::BitMask)?.enabledValues(enumClass)
}
}
class BitMask(val value: Int)
interface Flags {
val bit: Int
fun toBitMask() = BitMask(bit)
fun <T> BitMask.enabledValues(enumClass: Class<T>): List<T>? where T : Enum<T>, T : Flags? {
return enumClass.enumConstants?.filter(::hasFlag)
}
infix fun <T : Flags?> BitMask.hasFlag(flag: T): Boolean {
if (value == 0 || (value > 0 && flag?.bit == 0)) {
return false
}
return true
}
Maybe the logic in hasFlag is wrong, because I think that just gets every enum if it isn't 0.
Answer was to replace return true, with:
if (flag?.bit?.toByte() == null) {
return false
}
return (this.value.toByte().and(flag.bit.toByte()) == flag.bit.toByte())
This is basically: bit & mask == bit

How do I make a delegate apply to all properties in a class?

I have a class, A, that needs to be marked as dirty anytime one of its properties is changed.
After reviewing the Kotlin docs, I know I need a delegate. So far I have:
abstract class CanBeDirty {
var isDirty = false
}
class A(
// properties getting set in constructor here
) : CanBeDirty {
var property1: String by DirtyDelegate()
var property2: Int by DirtyDelegate()
var property3: CustomObject by DirtyDelegate()
}
class DirtyDelegate() {
operator fun getValue(thisRef: CanBeDirty, property: KProperty<*>): Resource {
return valueOfTheProperty
}
operator fun setValue(thisRef: CanBeDirty, property: KProperty<*>, value: Any?) {
if (property != value) {
thisRef.isDirty = true
//set the value
}
else {
//don't set the value
}
}
}
I believe the lack of setting has something to do with vetoable() but the examples I see in Kotlin documentation don't really show me how to do this with a fully formed class Delegate (and I'm just not that up to speed on Kotlin syntax, honestly).
Your delegate class needs its own property to store the value it will return. And if you don't want to deal with uninitialized values, it should also have a constructor parameter for the initial value. You don't have to implement ReadWriteProperty, but it allows the IDE to autogenerate the correct signature for the two operator functions.
class DirtyDelegate<T>(initialValue: T): ReadWriteProperty<CanBeDirty, T> {
private var _value = initialValue
override fun getValue(thisRef: CanBeDirty, property: KProperty<*>): T {
return _value
}
override fun setValue(thisRef: CanBeDirty, property: KProperty<*>, value: T) {
if (_value != value) {
_value = value
thisRef.isDirty = true
}
}
}
Since this takes an initial value parameter, you have to pass it to the constructor:
class A: CanBeDirty() {
var property1: String by DirtyDelegate("")
var property2: Int by DirtyDelegate(0)
var property3: CustomObject by DirtyDelegate(CustomObject())
}
If you wanted to set an initial value based on something passed to the constructor, you could do:
class B(initialName: String): CanBeDirty() {
var name by DirtyDelegate(initialName)
}

Union types / extension interfaces

I have several data class with fields, which are used in forms and need them to have a method return true if any of the fields has been filled.
I don't want to rewrite this for all the classes, so I'm doing it like this at the moment:
data class Order(var consumer: String, var pdfs: List<URI>): Form {
override val isEmpty(): Boolean
get() = checkEmpty(consumer, pdfs)
}
data class SomethingElse(var str: String, var set: Set<String>): Form {
override val isEmpty(): Boolean
get() = checkEmpty(str, set)
}
interface Form {
val isEmpty: Boolean
fun <T> checkEmpty(vararg fields: T): Boolean {
for (f in fields) {
when (f) {
is Collection<*> -> if (!f.isEmpty()) return false
is CharSequence -> if (!f.isBlank()) return false
}
}
return true;
}
}
This is obviously not very pretty nor type-safe.
What's a more idiomatic way of doing this, without abstracting every property into some kind of Field-type?
Clarification: What I'm looking for is a way to get exhaustive when, for example by providing all the allowed types (String, Int, List, Set) and a function for each to tell if they're empty. Like an "extension-interface" with a method isEmptyFormField.
It's kinda hacky but should work.
Every data class creates set of method per each constructor parameters. They're called componentN() (where N is number starting from 1 indicating constructor parameter).
You can put such methods in your interface and make data class implicitly implement them. See example below:
data class Order(var consumer: String, var pdfs: List) : Form
data class SomethingElse(var str: String, var set: Set) : Form
interface Form {
val isEmpty: Boolean
get() = checkEmpty(component1(), component2())
fun checkEmpty(vararg fields: T): Boolean {
for (f in fields) {
when (f) {
is Collection -> if (!f.isEmpty()) return false
is CharSequence -> if (!f.isBlank()) return false
}
}
return true;
}
fun component1(): Any? = null
fun component2(): Any? = null
}
You can also add fun component3(): Any? = null etc... to handle cases with more that 2 fields in data class (e.g. NullObject pattern or handling nulls directly in your checkEmpty() method.
As I said, it's kinda hacky but maybe will work for you.
If all you are doing is checking for isEmpty/isBlank/isZero/etc. then you probably don't need a generic checkEmpty function, etc.:
data class Order(var consumer: String, var pdfs: List<URI>) : Form {
override val isEmpty: Boolean
get() = consumer.isEmpty() && pdfs.isEmpty()
}
data class SomethingElse(var str: String, var set: Set<String>) : Form {
override val isEmpty: Boolean
get() = str.isEmpty() && set.isEmpty()
}
interface Form {
val isEmpty: Boolean
}
However, if you are actually do something a bit more complex then based on your added clarification I believe that "abstracting every property into some kind of Field-type" is exactly what you want just don't make the Field instances part of each data class but instead create a list of them when needed:
data class Order(var consumer: String, var pdfs: List<URI>) : Form {
override val fields: List<Field<*>>
get() = listOf(consumer.toField(), pdfs.toField())
}
data class SomethingElse(var str: String, var set: Set<String>) : Form {
override val fields: List<Field<*>>
get() = listOf(str.toField(), set.toField())
}
interface Form {
val isEmpty: Boolean
get() = fields.all(Field<*>::isEmpty)
val fields: List<Field<*>>
}
fun String.toField(): Field<String> = StringField(this)
fun <C : Collection<*>> C.toField(): Field<C> = CollectionField(this)
interface Field<out T> {
val value: T
val isEmpty: Boolean
}
data class StringField(override val value: String) : Field<String> {
override val isEmpty: Boolean
get() = value.isEmpty()
}
data class CollectionField<out C : Collection<*>>(override val value: C) : Field<C> {
override val isEmpty: Boolean
get() = value.isEmpty()
}
This gives you type-safety without changing your data class components, etc. and allows you to "get exhaustive when".
You can use null to mean "unspecified":
data class Order(var consumer: String?, var pdfs: List<URI>?) : Form {
override val isEmpty: Boolean
get() = checkEmpty(consumer, pdfs)
}
data class SomethingElse(var str: String?, var set: Set<String>?) : Form {
override val isEmpty: Boolean
get() = checkEmpty(str, set)
}
interface Form {
val isEmpty: Boolean
fun <T> checkEmpty(vararg fields: T): Boolean = fields.all { field -> field == null }
}
The idea here is the same as that of an Optional<T> in Java but without the extra object, etc.
You now have to worry about null safety but if your fields are meant to have a concept of absent/empty then this seems appropriate (UsingAndAvoidingNullExplained ยท google/guava Wiki).