I have a case class Foo
defined below. I want to override the behavior of ==
in that, so that the last element (optBar
) is ignored in the comparison. Here is what I have tried and it seems to work.
case class Bar(i:Int)
case class Foo(i:Int, s:String, optBar:Option[Bar]) {
override def equals(o:Any) = o match {
case Foo(`i`, `s`, _) => true
case _ => false
}
override def hashCode = i.hashCode*997 ^ s.hashCode * 991
}
val b = Bar(1)
val f1 = Foo(1, "hi", Some(b))
val f2 = Foo(1, "hi", None)
f1 == f2 // true
What I want to know is if the method of creating hashCode
is correct. I got it from this link.
IEquatable<T> interface by providing a type-specific Equals method. This is where the actual equivalence comparison is performed. For example, you might decide to define equality by comparing only one or two fields in your type.
In C#, Equals(String, String) is a String method. It is used to determine whether two String objects have the same value or not. Basically, it checks for equality. If both strings have the same value, it returns true otherwise returns false.
Plain Vanilla Operator == The most common way to compare objects in C# is to use the == operator. For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise.
Your hashCode definition is correct as in that it complies with the equals/hashCode contract. But I think
override def hashCode = (i, s).##
is nicer to read.
To clarify what this does: ## is just a convenience method on scala.Any that calls hashCode, but properly deals with null and some corner cases related to primitives.
val x: String = null
x.## // works fine. returns 0
x.hashCode // throws NullPointerException
So (i, s).## creates a tuple of i and s (which has a well-defined hashCode method) and then returns its hash code. So you don't have to manually write a hash code method involving MurmurHash etc. By the way: this will also properly work if one of the elements of the tuple is null, whereas a hand-written hash method like the one in the question might throw a NPE.
However, in my experience if you want to modify any of the things that a case class provides for you, you don't really want a case class. Also, overriding equality to not take into account some of the data might seem a clever idea at some point, but it can lead to some very confusing behavior.
You can also remove the optBar
from the case class definition and create a constructor with the three parameters. To avoid having to use the new
keyword when you want to use that constructor you can create a companion object.
case class Bar(i:Int)
case class Foo(i:Int, s:String) {
var optBar: Option[Bar] = None
def this(i:Int, s:String, optBar:Option[Bar]) {
this(i, s)
this.optBar = optBar
}
}
object Foo {
def apply(i:Int, s:String, optBar:Option[Bar]) =
new Foo(i, s, optBar)
}
val b = Bar(1)
val f1 = Foo(1, "hi", Some(b))
val f2 = Foo(1, "hi", None)
f1 == f2 // true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With