Item 8: Overriding Equals
Don't override the equals method if
- Each instance of the class is inherently unique
- Superclass has overridden equals method
- When the package is private
override fun equals(other: Any?): Boolean {
throw AssertionError() // Method is never called
}
Override the equals method if
- Logical equality is important
Equals method implements an equivalence relation:
Reflexive: For any non-null reference value x,
x.equals(x)must returntrueSymmetric: For any non-null reference values x and y,
x.equals(y)must returntrueif and only ify.equals(x)returnstrue.Transitive: For any non-null reference values x, y, z,
if x.equals(y)returnstrueandy.equals(z)returnstrue, thenx.equals(z)must returntrue.Consistent: For any non-null reference values x and y,
multiple invocations of
x.equals(y)consistently returntrueor consistently returnfalse, provided no information used in equals comparisons on the objects is modified.For any non-null reference value x,
x.equals(null)must returnfalse.
Do not write equals method on unreliable resources.
Steps to writing a high qualityequalsmethod
- Use the
===operator to check if the argument is a reference to the object - Check if the
javaclassof both the objects are the same - Cast the argument to the correct type
- Every significant field in the class needs to be checked for logical equality to the corresponding field of the object
- Check if the method obeys symmetry, transitivity and if it is consistent.
- Also, make sure to override the
hashCodemethod.
In Kotlin, we get all this for free using the
data classprovided by default
Item 9: Overriding Hashcode
There is a rule to always overridehashCodewheneverequalsis overridden. If this is not obeyed, then this would result in a violation of the contract of the Object.hashCode method and will prevent the class from functioning properly in conjunction with all the has-based collections.
The vital part of writing thehashCodemethod relies on the fact thatequal objects must have equal hash codes. A good hashCode should always produce different hashcodes for unequal objects.
In Kotlin, we get all this for free using the
data classprovided by default
Steps to write a high qualityhashCodemethod
- Store a constant non-zero value calculated from any attribute of the class using its superclass method
var result = fName.hashCode()
For each significant field f (All field defined in theequalsmethod), do the following:
- Compute an
Int hashCode cfor the field- Boolean:
f ?: 0 - Byte, Char, Short, Float, Double, Int:
f.toInt() - Long:
(f ^ (f >>> 32)).toInt() - The class's
equalsmethod compares the field by recursively invoking equals and invoke thehashCodeon the field. - Array: Each element is treated as a separate field by applying the rules recursively.
- Boolean:
In Kotlin, we get all this for free using the
data classprovided by default
Item 10: Overriding toString
A goodtoStringimplementation makes the class much more pleasant to use. It clearly displays the most significant information required in a class object. If thetoStringmethod is not overridden, then printing the object would return the class name followed by the unsigned hexadecimal representation of the hashcode.
If there is a specific format of thetoStringthen mention them in the documentation. If not then make a specific comment about thetoStringmethod.
Also, provide programmatic access to all the information contained in the value returned bytoString.
In Kotlin, we get all this for free using the
data classprovided by default
Item 11: Cloning Objects
Cloning in kotlin is as easy as calling the copy method from the data class. The method provided by the data class offers the following:
- x.copy() !== x
- x.javaClass == x.copy().javaClass
- x.copy().equals(x)
This method provided by Kotlin itself satisfies all the requirements that are requested by the contract of implementing the Cloneable interface in Java to expose the protected clone() method in the object class.
And in this method, we could also provide named arguments as to what should be different from the data class it is cloned from. The data class in kotlin uses the copy constructor approach from Java to implement it's cloning facility.
It uses a copy constructor and a static factory which provides a lot more robustness over implementing the Cloneable interface. Such as
- Doesn't rely on a risk-prone extralinguistic object creation mechanism
- Doesn't demand unenforceable adherence to not-so documented conventions
- Doesn't conflict with vals
- Doesn't throw checked exceptions
- Doesn't require casts
- Add interface like functionality since Cloneable doesn't have a public
clonemethod
Item 12: Implementing Comparable and Using Comparators
Implementing the comparable interface in the class provides a "natural" way of ordering the objects created by the class. ThecompareTomethod provided by the comparable interface is used to sort the collection in the way specified. The general contract of thecompareTomethod is similar to that of theequalsmethod.
- Compare
thisobject with the other object provided - Returns a negative integer if the object is less than the object it is compared to.
- Returns zero if they are equal
- Returns a positive integer if the object is greater than the object it is compared to.
- Throws
ClassCastExceptionif the specified object's type prevents it from being compared to this Object.
The contract specification goes as follows
- x.compareTo(y) == -y.compareTo(x) for all x and y
- Transitivity: (x.compareTo(y) > 0 && y.compareTo(z) > 0)
- if x.compareTo(y) == 0, then x.compareTo(z) == y.compareTo(z)
- Recommendation: (x.compareTo(y) == 0) == (x.equals(y))
compareByusing Comparator
ThecompareByfunction available in Kotlin is used to sort an object with multiple fields with the use of comparators and method references. This creates a comparator using the sequence of functions to calculate a result of the comparison. This is called in the sequence that we need the list to be ordered.
// Using the it operator
val sortedListOfMovies: List<Movies> = list.sortedWith(compareBy({ it.rating }, { it.year }))
// Using method references
val sortedListOfMovies: List<Movie> = moviesList.sortedWith(compareBy(Movie::rating, Movie::year))