Consider the following code:
import scala.collection.mutable
case class A(b: Int) {
override def equals(obj: Any): Boolean = {
println("called")
obj match {
case o: A => b == o.b
case _ => false
}
}
}
val set = mutable.Set.empty[A]
val a1 = A(1)
set.add(a1)
println(set.contains(A(1)))
println(set.contains(A(2)))
Why the second call of set.contains doesn't print out "called"? Can someone explain how set identify two objects being equal?
As #LuisMiguelMejíaSuárez mentioned in the comments, A mutable Set is backed by a HashSet. It is well elaborated at What issues should be considered when overriding equals and hashCode in Java?
Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
This is because hashCode is a prior check, which when fails equals will definitely fail as well.
From What code is generated for an equals/hashCode method of a case class? we can conclude that for case classes hashCode and equals are overridden for case classes, using productPrefix which means the first set of elements in the case class. For example, if we have:
case class c(a: A)(b: B)
The hashCode and equals will be calculated only based on a. In your case, the hasCode method is calculated based on gender, which is different for Person(1) and Person(2). Therefore there is no need to check equals, which suppose to fail as well.
A tip from What issues should be considered when overriding equals and hashCode in Java?
In practice:
If you override one, then you should override the other.
Use the same set of fields that you use to compute equals() to compute hashCode().
Related
Is there a better explanation than "this is how it works". I mean I tried this one:
class TestShortMatch[T <: AnyRef] {
def foo(t: T): Unit = {
val f = (_: Any) match {
case Val(t) => println(t)
case Sup(l) => println(l)
}
}
class Val(t: T)
class Sup(l: Number)
}
and compiler complaints:
Cannot resolve symbol 'Val'
Cannot resolve symbol 'Sup'
Of course if I add case before each of the classes it will work fine. But what is the reason? Does compiler make some optimization and generate a specific byte-code?
The reason is twofold. Pattern matching is just syntactic sugar for using extractors and case classes happen to give you a couple methods for free, one of which is an extractor method that corresponds to the main constructor.
If you want your example above to work, you need to define an unapply method inside objects Val and Sup. To do that you'd need extractor methods (which are only defined on val fields, so you'll have to make your fields vals):
class Val[T](val t: T)
class Sup(val l: Number)
object Val {
def unapply[T](v: Val[T]): Option[T] = Some(v.t)
}
object Sup {
def unapply(s: Sup): Option[Number] = Some(s.l)
}
And which point you can do something like val Val(v) = new Val("hi"). More often than not, though, it is better to make your class a case class. Then, the only times you should be defining extra extractors.
The usual example (to which I can't seem to find a reference) is coordinates:
case class Coordinate(x: Double, val: Double)
And then you can define a custom extractors like
object Polar {
def unapply(c: Coordinate): Option[(Double,Double)] = {...}
}
object Cartesian {
def unapply(c: Coordinate): Option[(Double,Double)] = Some((c.x,c.y))
}
to convert to the two different representations, all when you pattern match.
You can use pattern matching on arbitrary classes, but you need to implement an unapply method, used to "de-construct" the object.
With a case class, the unapply method is automatically generated by the compiler, so you don't need to implement it yourself.
When you write match exp { case Val(pattern) => ... case ... }, that is equivalent to something like this:
match Val.unapply(exp) {
case Some(pattern) =>
...
case _ =>
// code to match the other cases goes here
}
That is, it uses the result of the companion object's unapply method to see whether the match succeeded.
If you define a case class, it automatically defines a companion object with a suitable unapply method. For a normal class it doesn't. The motivation for that is the same as for the other things that gets automatically defined for case classes (like equals and hashCode for example): By declaring a class as a case class, you're making a statement about how you want the class to behave. Given that, there's a good chance that the auto generated will do what you want. For a general class, it's up to you to define these methods like you want them to behave.
Note that parameters for case classes are vals by default, which isn't true for normal classes. So your class class Val(t: T) doesn't even have any way to access t from the outside. So it isn't even possible to define an unapply method that gets at the value of t. That's another reason why you don't get an automatically generated unapply for normal classes: It isn't even possible to generate one unless all parameters are vals.
This is a situation I have encountered frequently, but I have not been able to find a solution yet.
Suppose you have a list of persons and you just want to verify the person names.
This works:
persons.map(_.name) should contain theSameElementsAs(List("A","B"))
Instead, I would rather write this like
val toName: Person => String = _.name
persons should contain theSameElementsAs(List("A","B")) (after mapping toName)
because this is how you would say this.
Sometimes however, you'd like to use a custom matcher which matches more than just one property of the object. How would it be possible to use
persons should contain(..)
syntax, but somehow be able to use a custom matcher?
Both these situations I could easily solve using JUnit or TestNG using Hamcrest matchers, but I have not found a way to do this with ScalaTest.
I have tried to use the 'after being' syntax from the Explicitly trait, but that's not possible since this takes a 'Normalization' which defines that the 'normalized' method uses the same type for the argument and return type. So it's not possible to change a Person to a String.
Also I have not succeeded yet in implementing an 'Explicitly' like trait because it does not like the Equality[.] type I return and/or it does not know anymore what the original list type was, so using '_.name' does not compile.
Any suggestions are welcome.
You can manage something similar via the word decided and moderate abuse of the Equality trait. This is because the Equality trait's areEqual method takes a parameter of the generic type and one of type Any, so you can use that to compare Person with String, and decided by simply takes an Equality object which means you don't have to futz around with Normality.
import org.scalactic.Equality
import org.scalatest.{FreeSpec, Matchers}
final class Test extends FreeSpec with Matchers {
case class Person(name: String)
val people = List(Person("Alice"), Person("Eve"))
val namesBeingEqual = MappingEquality[Person, String](p => p.name)
"test should pass" in {
(people should contain theSameElementsAs List("Alice", "Eve"))(
decided by namesBeingEqual)
}
"test should fail" in {
(people should contain theSameElementsAs List("Alice", "Bob"))(
decided by namesBeingEqual)
}
case class MappingEquality[S, T](map: S => T) extends Equality[S] {
override def areEqual(s: S, b: Any): Boolean = b match {
case t: T => map(s) == t
case _ => false
}
}
}
I'm not sure I'd say this is a good idea since it doesn't exactly behave in the way one would expect anything called Equality to behave, but it works.
You can even get the beingMapped syntax you suggest by adding it to after via implicit conversion:
implicit class AfterExtensions(aft: TheAfterWord) {
def beingMapped[S, T](map: S => T): Equality[S] = MappingEquality(map)
}
}
I did try getting it work with after via the Uniformity trait, which has similar methods involving Any, but ran into problems because the normalization is the wrong way around: I can create a Uniformity[String] object from your example, but not a Uniformity[Person] one. (The reason is that there's a normalized method returning the generic type which is used to construct the Equality object, meaning that in order to compare strings with strings the left-side input must be a string.) This means that the only way to write it is with the expected vs actual values in the opposite order from normally:
"test should succeed" in {
val mappedToName = MappingUniformity[Person, String](person => person.name)
(List("Alice", "Eve") should contain theSameElementsAs people)(
after being mappedToName)
}
case class MappingUniformity[S, T](map: S => T) extends Uniformity[T] {
override def normalizedOrSame(b: Any): Any = b match {
case s: S => map(s)
case t: T => t
}
override def normalizedCanHandle(b: Any): Boolean =
b.isInstanceOf[S] || b.isInstanceOf[T]
override def normalized(s: T): T = s
}
Definitely not how you'd usually want to write this.
use inspectors
forAll (xs) { x => x should be < 3 }
Question
In Scala, is there a way to write a match clause which matches an object by it's class, but does not match any extending classes?
Motivation
It may seem pedantic.. it is.
Anyway, I thought of it when I saw the equals function generated by IntelliJ:
class Position(val x: Float, val y: Float) {
def canEqual(other: Any): Boolean = other.isInstanceOf[Position]
override def equals(other: Any): Boolean = other match {
case that: Position =>
(that canEqual this) &&
x == that.x &&
y == that.y
case _ => false
}
// generated hashCode omitted for brevity
}
Here we can see that other is class matched to be a Position, and then canEqual is used to put an upper bound on the match. That is, other could still be, say, a Position3D, which shouldn't equal a Position even if the x and y coordinates are the same.
This makes sense, but I wonder if some form of Scala wizardry would let us explicitly match Position classes and not subclasses.
There's a reason the auto-generated equals method doesn't do what you're suggesting. The canEqual convention is a common and well-documented one, because it allows for handling a broader set of common use cases.
You don't always want to exclude all sub classes when implementing an equals method, only ones that override equals. If, as you described, there was a Position3D class extending Position, and Position3D had an additional field z that was part of the Position3D equals method, then of course that Position3D should not be equal to any normal 2D position.
But what if for some reason a user wants to instantiate an anonymous subclass of Position?
val pos = new Position(1,2){}
or
trait Foo
val pos = new Position(1,2) with Foo
This position should probably be considered equal to new Position(1,2). Disallowing this equality could in some situations be tedious, if the mixed-in functionality is trivial or just for some particular local-scope use case. To require, as the other response suggested, other.getClass == classOf[Position], a client API mixing in a trait or for some reason creating an anonymous class might find they're creating positions that are not even equal to themselves.
Consider this code:
class Position(val x:Int, val y:Int) {
override def equals(other:Any) = other match {
case o:Position if o.getClass==classOf[Position] && o.x==x && o.y==y => true
case _ => false
}
}
trait Foo
val p1 = new Position(1,2) with Foo
val p2 = new Position(1,2) with Foo
p1 == p2
the result is false
see:
http://www.artima.com/lejava/articles/equality.html
Try:
other match {
case that: Position if that.getClass == classOf[Postion] => // or just getClass
(that canEqual this) &&
x == that.x &&
y == that.y
case: that: Postion => false //this case can be removed
case _ => false
}
I have a question concerning Scala case classes. I have a case class that is a composition of other types as below:
case class MyCaseClass(typeA: TypeA, typeB: TypeB) {
override def equals(obj: scala.Any): Boolean = super.equals(obj) // generated by intelliJ
}
Both my TypeA and TypeB are normal Scala classes and what I want is that, two instances of MyCaseClass is equal only if all the properties of typeA and typeB are equal. I could of course do this check in the equals method directly in my case class like below:
case class MyCaseClass(typeA: TypeA, typeB: TypeB) {
....
def equals(obj: Any): Boolean = obj match {
case myCase: MyCaseClass => myCase.typeA.equals(myCase.typeB)
case _ => false
}
}
With respect to the example snippet above, I have the following questions:
If my TypeA is a case class and my TypeB is a normal Scala class, would this means that I have to implement equals method just in TypeB where in my TypeA being a case class would get the equals method by default and I do not have to provide one?
My MyCaseClass would be used in such a way that I have a sequence of MyCaseClass and a primitive value (Double or Integer) as a tuple Seq(MyCaseClass, Double). Now if I do a groupBy operation on MyCaseClass from the Sequence, will they be groupedBy based on the equals method?
You're inheriting nothing, so the super.equals you're getting, possibly that of Product2 (assuming it has a meaningful one of its own) or scala.ScalaObject, is probably not what you want.
But the real question is why are you altering the semantics of the compiler-supplied equals?
Lastly, if there's any doubt in your mind (as evidenced by asking a question on Stack Overflow) when it comes to writing your own equals, it behooves you to read chapter 28, "Equality," of Programming in Scala. Hint: Writing truly correct equals is not trivial. That chapter is 26 pages long! (Note: I have only the 1st ed. at hand.)
Frankly, as infrequently as I need to write my own equals, I end up consulting that chapter every time I do.
I have an immutable Set of a class, Set[MyClass], and I want to use the Set methods intersect and diff, but I want them to test for equality using my custom equals method, rather than default object equality test
I have tried overriding the == operator, but it isn't being used.
Thanks in advance.
Edit:
The intersect method is a concrete value member of GenSetLike
spec: http://www.scala-lang.org/api/current/scala/collection/GenSetLike.html
src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/GenSetLike.scala#L1
def intersect(that: GenSet[A]): Repr = this filter that
so the intersection is done using the filter method.
Yet another Edit:
filter is defined in TraversableLike
spec: http://www.scala-lang.org/api/current/scala/collection/TraversableLike.html
src: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/collection/TraversableLike.scala#L1
def filter(p: A => Boolean): Repr = {
val b = newBuilder
for (x <- this)
if (p(x)) b += x
b.result
}
What's unclear to me is what it uses when invoked without a predicate, p. That's not an implicit parameter.
equals and hashCode are provided automatically in case class only if you do not define them.
case class MyClass(val name: String) {
override def equals(o: Any) = o match {
case that: MyClass => that.name.equalsIgnoreCase(this.name)
case _ => false
}
override def hashCode = name.toUpperCase.hashCode
}
Set(MyClass("xx"), MyClass("XY"), MyClass("xX"))
res1: scala.collection.immutable.Set[MyClass] = Set(MyClass(xx), MyClass(XY))
If what you want is reference equality, still write equals and hashCode, to prevent automatic generation, and call the version from AnyRef
override def equals(o: Any) = super.equals(o)
override def hashCode = super.hashCode
With that:
Set(MyClass("x"), MyClass("x"))
res2: scala.collection.immutable.Set[MyClass] = Set(MyClass(x), MyClass(x))
You cannot override the ==(o: Any) from AnyRef, which is sealed and always calls equals. If you tried defining a new (overloaded) ==(m: MyClass), it is not the one that Set calls, so it is useless here and quite dangerous in general.
As for the call to filter, the reason it works is that Set[A] is a Function[A, Boolean]. And yes, equals is used, you will see that function implementation (apply) is a synonymous for contains, and most implementations of Set use == in contains (SortedSet uses the Ordering instead). And == calls equals.
Note: the implementation of my first equals is quick and dirty and probably bad if MyClass is to be subclassed . If so, you should at the very least check type equality (this.getClass == that.getClass) or better define a canEqual method (you may read this blog by Daniel Sobral)
You'll need to override .hashCode as well. This is almost always the case when you override .equals, as .hashCode is often used as a cheaper pre-check for .equals; any two objects which are equal must have identical hash codes. I'm guessing you're using objects whose default hashCode does not respect this property with respect to your custom equality, and the Set implementation is making assumptions based on the hash codes (and so never even calling your equality operation).
See the Scala docs for Any.equals and Any.hashCode: http://www.scala-lang.org/api/rc/scala/Any.html
This answer shows a custom mutable Set with user-defined Equality. It could be made immutable by replacing the internal store with a Vector and returning a modified copy of itself upon each operation
"It is not possible to override == directly, as it is defined as a final method in class Any. That is, Scala treats == as if were defined as follows in class Any:
final def == (that: Any): Boolean =
if (null eq this) {null eq that} else {this equals that}
" from Programming In Scala, Second Edition