Kotlin equals and hash code generator

15,376

Solution 1

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.

Solution 2

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
    }

}

Solution 3

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.

Share:
15,376

Related videos on Youtube

David
Author by

David

Updated on September 14, 2022

Comments

  • David
    David about 1 year

    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.

  • Some Guy
    Some Guy almost 5 years
    You can make value a val if you use the lazyinit attribute.