trait Equality[A] extends Equivalence[A]
Defines a custom way to determine equality for a type when compared with another value of type Any.
Equality enables you to define alternate notions of equality for types that can be used
with ScalaUtil's === and !== syntax and ScalaTest's matcher syntax.
For example, say you have a case class that includes a Double value:
scala> case class Person(name: String, age: Double) defined class Person
Imagine you are calculating the age values in such as way that occasionally tests
are failing because of rounding differences that you actually don't care about. For example, you
expect an age of 29.0, but you're sometimes seeing 29.0001:
scala> import org.scalactic._
import org.scalactic._
scala> import TripleEquals._
import TripleEquals._
scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
res0: Boolean = false
The === operator looks for an implicit Equality[L], where L is the left-hand type: in this
case, Person. Because you didn't specifically provide an implicit Equality[Person], === will fall back on
default equality, which will call Person's equals method. That equals method, provided by the Scala compiler
because Person is a case class, will declare these two objects unequal because 29.001 does not exactly equal 29.0.
To make the equality check more forgiving, you could define an implicit Equality[Person] that compares
the age Doubles with a tolerance, like this:
scala> import Tolerance._
import Tolerance._
scala> implicit val personEq =
| new Equality[Person] {
| def areEqual(a: Person, b: Any): Boolean =
| b match {
| case p: Person => a.name == p.name && a.age === p.age +- 0.0002
| case _ => false
| }
| }
personEq: org.scalactic.Equality[Person] = $anon$1@2b29f6e7
Now the === operator will use your more forgiving Equality[Person] for the equality check instead
of default equality:
scala> Person("Joe", 29.0001) === Person("Joe", 29.0)
res1: Boolean = true
Default equality
Scalactic defines a default Equality[T] for all types T whose areEqual method works by first
calling .deep on any passed array, then calling == on the left-hand object, passing in the right-hand object.
You can obtain a default equality via the default method of the Equality companion object,
or from the defaultEquality method defined in TripleEqualsSupport.
About equality and equivalence
The Equality trait represents the Java Platform's native notion of equality, as expressed in the signature and contract of
the equals method of java.lang.Object. Essentially, trait Equality enables you to write alternate
equals method implementations for a type outside its defining class.
In an equals method, the left-hand type is known to be the type of this, but
the right-hand type is Any.
As a result, you would normally perform a runtime type test to determine whether the right-hand object is of an appropriate type for equality,
and if so, compare it structurally for equality with the left-hand (this) object.
An an illustration, here's a possible equals
implementation for the Person case class shown in the earlier example:
override def equals(other: Any): Boolean = other match { case p: Person => name = p.name && age = p.age case _ => false }
The areEquals method of Equality[T] is similar. The left-hand type is known to be T, but the right-hand type is Any, so
normally you'd need to do a runtime type test in your areEqual implementation.
Here's the areEqual method implementation from the earlier Equality[Person] example:
def areEqual(a: Person, b: Any): Boolean = b match { case p: Person => a.name == p.name && a.age === p.age +- 0.0002 case _ => false }
Equality is used by TripleEquals, which enforces no type constraint between the left and right values, and the
equal, be, and contain syntax of ScalaTest Matchers.
By contrast, TypeCheckedTripleEquals
and ConversionCheckedTripleEquals use an Equivalence.
Equivalence differs from Equality in that both the left and right values are of the same type. Equivalence works for
TypeCheckedTripleEquals because the type constraint enforces that the left type is a subtype or supertype of (or the same type as) the right
type, and it widens the subtype to the supertype. So ultimately, both left and right sides are of the supertype type. Similarly, Equivalence
works for ConversionCheckedTripleEquals because the type constraint enforces that an implicit conversion
exists from either the left type to the right type, or the right type to the left type, and it always converts one
type to the other using the implicit conversion. (If both types are the same type, the identity implicit conversion
from Predef is used.) Because of the conversion, both left and right sides are ultimately of the
converted-to type. Here's an example of how writing an Equivalence's areEquivalent
method might look:
def areEquivalent(a: Person, b: Person): Boolean = a.name == b.name && a.age === b.age +- 0.0002
Scalactic provides both Equality and Equivalence because the Any in
Equality can sometimes make things painful. For example, in trait
TolerantNumerics,
a single generic factory method can produce Equivalences for any Numeric type,
but because of the Any, a separate factory method must be defined to produce an Equality
for each Numeric type.
If you just want to customize the notion of equality for ===
used in Boolean expressions, you can work with Equivalences instead of Equalitys.
If you do chose to write the more general Equalitys, they can be used wherever an Equivalence
is required, because Equality extends Equivalence, defining a final implementation of
areEquivalent that invokes areEqual.
Note: The Equality type class was inspired in part by the Equal type class of the
scalaz project.
- A
the type whose equality is being customized
- Source
- Equality.scala
- Alphabetic
- By Inheritance
- Equality
- Equivalence
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Abstract Value Members
- abstract def areEqual(a: A, b: Any): Boolean
Indicates whether the objects passed as
aandbare equal.Indicates whether the objects passed as
aandbare equal.- a
a left-hand value being compared with another (right-hand-side one) for equality (e.g.,
a == b)- b
a right-hand value being compared with another (left-hand-side one) for equality (e.g.,
a == b)- returns
true if the passed objects are "equal," as defined by this
Equalityinstance
Concrete Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def areEquivalent(a: A, b: A): Boolean
A final implementation of the
areEquivalentmethod ofEquivalencethat just passesaandbtoareEqualand returns the result.A final implementation of the
areEquivalentmethod ofEquivalencethat just passesaandbtoareEqualand returns the result.This method enables any
Equalityto be used where anEquivalenceis needed, such as the implicit enabling methods ofTypeCheckedTripleEqualsandConversionCheckedTripleEquals.- a
a left-hand value being compared with another, right-hand, value for equality (e.g.,
a == b)- b
a right-hand value being compared with another, left-hand, value for equality (e.g.,
a == b)- returns
true if the passed objects are "equal," as defined by the
areEqualmethod of thisEqualityinstance
- Definition Classes
- Equality → Equivalence
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native()
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable])
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()