Object Equality In Scala


         Ruchi Agarwal
       Software Consultant
            Knoldus
Object Equality In Java & Scala:

- Two equality comparisons:
    1. == Operator
    2. Equals()

- The == operator, which is the natural equality for value types and object indentity for
   Refernce types in Java.

// This is Java
boolean isEqual(int x, int y){

    return x == y;                         // Natural equality on value types

}
System.out.println(isEqual ( 421, 421));       //Output: True

//java.lang.Integer(Object)

boolean isEqual(Integer x, Integer y){

    return x == y;                         // Reference equality on reference types
}
System.out.println(isEqual ( 421, 421));       //Output: False
- The == equality is reserved in Scala for the ā€œ Natural ā€ equality of each type. The
  equality operation == in scala is designed to be transparent with respect to the type's
  representation.

- For value types, it is the natural (numeric or boolean) equality.

- For reference types, == is treated as an alias of the equals() inherited from object.

//This is Scala

scala> def isEqual(x : Int , y : Int) = x == y
isEqual: (x: Int, y: Int )Boolean

scala> isEqual( 421 , 421)
res8: Boolean = true

scala> def isEqual(x : Any , y : Any) = x == y
isEqual: (x: Any , y: Any)Boolean

scala> isEqual( 421 , 421)
res8: Boolean = true
String Comparisons:
- Scala, never fall into Java's well-known trap concerning string comparisons.

//This is Scala

scala> val x=ā€œabcdā€.substring(2)
x: java.lang.String = cd

scala> val y=ā€œabcdā€.substring(2)
y: java.lang.String = cd

scala> x == y
res7: Boolean = true

//This is Java
boolean isEquals(String x, String y){

    return x.substring(2) == y.substring(2);     //return x.substring(2).equals(y.substring(2));
                                                 //Output: true
}
String s1="abcd";
String s2="abcd";
System.out.println(isEquals(s1, s2));            //Output: false
Reference Equality:

- For refernce equality, Scala's class AnyRef defines an additional eq method, which
  cannot be overridden and is implemented as reference equality(i. e., it behaves like ==
  in Java for reference types).

scala> val x=new String(ā€œabcā€)
x: java.lang.String = abc

scala> val x=new String("abc")
x: java.lang.String = abc

scala> x == y
res13: Boolean = true                                     // Value equality

scala> x eq y
res14: Boolean = false                                    // Reference Equality

- There's also the negation of eq , which is called ne.

 scala> x ne y
res14: Boolean = true
- For refernce equality, the behavior of == for new types can redefine by overriding the
  equals method, which is always inherited from class Any.

- It is not possible to override == directly, as it is defined as a final method in class Any.

// defination of == in Any class

final def == (that : Any) : Boolean =
     if ( null eq this) { null eq that }
           else { this equals that }
Writing an Equality Method:

 - Writing a correct equality method is surprisingly difficult in object-oriented languages.

 - This is problematic, because equality is at the basis of many other things.

 - Here are four common pitfalls that can cause inconsistent behavior when overriding
   equals:

     1. Defining equals with wrong signature

     2. Changing equals without also changing hashCode

     3. Defining equals in terms of mutable fields

     4. Failing to define equals as an equivalence relation
Pitfall #1: Defining equals with wrong signature
class Point ( val x:Int, val y :Int)    {

     def equals (other: Point): Boolean =          //Overloaded equals()
        this.x == other.x && this.y == other.y
 }

scala> val p1, p2 = new Point (1 , 2)
p1: Point = Point@1e0bb90
p2: Point = Point@139fb49

scala> val q= new Point( 2 , 3)
q: Point = Point@a44ec3

//Working OK
scala> p1 equals p2                    res:0 Boolean : true
scala> p1 equals q                     res:0 Boolean : false

//Trouble

scala> val p2a: Any = p2               // Alias of p2
p2a: Any = Point@139fb49

// p2a calling equals() of Any class
scala> p1 equals p2a                   res:0 Boolean : false
Solution: Correct Signature


 class Point ( val x:Int, val y :Int)         {

     //        A better definition:Overriding equals of Any class

      override def equals(other: Any) = other match {

              case that: Point => this.x == that.x && this.y == that.y

              case _ => false

          }
 }


 scala> val p2a: Any = p2                   // Alias of p2
 p2a: Any = Point@139fb49

 scala> p1 equals p2a                       res:0 Boolean : true
- A related pitfall is to define == with a wrong signature.

- If try to redefine == with the correct signature, which takes an argument of type Any, the
  compiler will give an error because you try to override a final method of type Any.

- However, newcomers to Scala sometimes make two errors at once: They try to override ==
  and they give it the wrong signature.

- For instance:

         def ==(other: Point): Boolean = // Don’t do this! (User-defined == method)

- In that case, the user-defined == method is treated as an overloaded variant of the
  same-named method class Any, and the program compiles.

- However, the behavior of the program would be just as dubious as if you had defined
  equals with the wrong signature.
Pitfall #2: Changing equals without also changing hashCode
  //Previously defined equals method

  class Point ( val x:Int, val y :Int)        {

      //        A better definition:Overriding equals of Any class

       override def equals(other: Any) = other match {

               case that: Point => this.x == that.x && this.y == that.y

               case _ => false
           }
  }

  scala> import scala.collection.mutable._
  import scala.collection.mutable._

  scala> val p1, p2 = new Point (1 , 2)
  p1: Point = Point@1bef480
  p2: Point = Point@1a6068c

  //storing p1 to collection
  scala> val coll = HashSet (p1)
  coll: scala.collection.mutable.Set[Point] = Set(Point@62d74e)

  scala> coll contains p2                         res2: Boolean = false
- In fact, this outcome is not 100% certain. We might also get true from the experiment.
  We can try with some other points with coordinates 1 and 2. Eventually, we’ll get one
  which is not contained in the set.



                                hashSet(p1) contains p2


                 val p2
    true         val p1                                            val p2
                                         false
                  hashSet                                       Hash Bucket
               Hash Bucket
                    Pitfall #2: Changing equals without also changing hashCode
- Wrong here is that Point redefined equals without also redefining hashCode.

- The problem was that the last implementation of Point violated the contract on hashCode as
   defined for class Any:

   If two objects are equal according to the equals method, then calling the
  hashCode method on each of the two objects must produce the same integer result.

- In fact, it’s well known in Java that hashCode and equals should always be redefined
   together.

- Furthermore, hashCode may only depend on fields that equals depends on.

- For the Point class, the following would be a suitable definition of hashCode:


      override def hashCode = 41 * (41 + x) + y                   // Redefine hashCode


- This is just one of many possible implementations of hashCode.

- Adding hashCode fixes the problems of equality when defining classes like Point.
class Point(val x: Int, val y: Int) {

    override def hashCode = 41 * (41 + x) + y               // Redefine hashCode

    override def equals(other: Any) = other match {         // Redefine equals()

        case that: Point => this.x == that.x && this.y == that.y
        case _ => false

    }

}

scala> val p1, p2 = new Point(1, 2)
p1: Point = Point@6bc
p2: Point = Point@6bc

scala> coll contains p2                  res2: Boolean = true
Pitfall #3: Defining equals in terms of mutable fields:
  - Consider the following slight variation of class Point:
    class Point2(var x: Int, var y: Int) {

         override def hashCode = 41 * (41 + x) + y                       // Redefine hashCode

         override def equals(other: Any) = other match { // Redefine equals()

              case that: Point2 => this.x == that.x && this.y == that.y
              case _ => false
         }
    }

    scala> val p = new Point(1, 2)                              p: Point = Point@2b

    scala> val coll = HashSet(p)
    coll: scala.collection.mutable.Set[Point] = Set(Point@2b)

    scala> coll contains p                                      res5: Boolean = true

    scala> p.x += 1                              // Changing p.x value

    scala> coll contains p                                      res7: Boolean = false

    scala> coll.elements contains p                             res7: Boolean = true
coll.elements contains p
                true


                                        p.x += 1
                                                                               After change value it
                                        (x:mutable)                            assign to wrong hash
                                                                                      bucket
             p.x=1                                               p.x=2
                                          coll contains p
             coll                               false         Hash Bucket
          Hash Bucket

                      Pitfall #3: Defining equals in terms of mutable fields


- In a manner of speaking, the point p ā€œdropped out of sightā€ in the set coll even though it
  still belonged to its elements.

- If you need a comparison that takes the current state of an object into account, you
  should usually name it something else, not equals.
Pitfall #4: Failing to define equals as an equivalence relation:
   - In scala, Any class specifies that equals must implement an equivalence relation on
   non-null objects:

       • It is reflexive: for any non-null value x , the expression x.equals(x) should return true.

       • It is symmetric: for any non-null values x and y, x.equals(y) should return true if and
                         only if y.equals(x) returns true.

       • It is transitive: for any non-null values x, y, and z, if x.equals(y) returns true and
                         y.equals(z) returns true, then x.equals(z) should return true.

       • It is consistent: for any non-null values x and y, multiple invocations of x.equals(y)
                           should consistently return true or consistently return false, provided
         no information used in equals comparisons on the objects is modified.

       • For any non-null value x, x.equals(null) should return false.

   - The definition of equals developed so far for class Point satisfies the contract for equals.

   - However, things become more complicated once subclasses are considered.
Symmetric Problem:
  class Point(val x: Int, val y: Int) {

      override    def hashCode = 41 * (41 + x) + y    // Redefine hashCode
      override    def equals(other: Any) = other match { // Redefine equals()
          case    that: Point => this.x == that.x && this.y == that.y
          case    _ => false
      }
  }

  object Color extends Enumeration {
      val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value
  }

  class ColoredPoint(x: Int, y: Int, val color: Color.Value)
         extends Point(x, y) {         // Problem: equals not symmetric

           override def equals(other: Any) = other match {
               case that: ColoredPoint =>this.color == that.color &&
                                         super.equals(that)
               case _ => false
           }
       }
  scala> val p = new Point(1, 2)                      p: Point = Point@2b
  scala> val cp = new ColoredPoint(1, 2, Color.Red)   cp: ColoredPoint =ColoredPoint@2b
  scala> p equals cp                                  res:0 Boolean : true
  scala> cp equals p                                  res:0 Boolean : false
Solution of Symmetric Problem but violated Transitivity contract:

  class Point(val x: Int, val y: Int) {

      override   def hashCode = 41 * (41 + x) + y    // Redefine hashCode
      override   def equals(other: Any) = other match { // Redefine equals()
          case   that: Point => this.x == that.x && this.y == that.y
          case   _ => false
      }
  }

  object Color extends Enumeration {
      val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value
  }

  class ColoredPoint(x: Int, y: Int, val color: Color.Value)
         extends Point(x, y) {         // Problem: equals not transitive

          override def equals(other: Any) = other match {
              case that: ColoredPoint =>this.color == that.color &&
                                    super.equals(that)
              case that:Point=>that equals this //new case to make symmetric
              case _ => false
          }
      }
  scala> p equals cp                            res:0 Boolean : true
  scala> cp equals p                            res:0 Boolean : true
- Here’s a sequence of statements that demonstrates transitive. Define a point and two
  colored points of different colors, all at the same position:
        scala> var redp=new ColoredPoint(1,2,Color.Red)
        redp: ColoredPoint = ColoredPoint@6bc

        scala> var bluep=new ColoredPoint(1,2,Color.Blue)
        bluep: ColoredPoint = ColoredPoint@6bc

        scala> var p=new Point(1,2)
        p: Point = Point@6bc

        scala> redp == p                                     res1: Boolean = true
        scala> p == bluep                                    res1: Boolean = true
        scala> redp == bluep                                 res1: Boolean = false


- Hence, the transitivity clause of equals’s contract is violated.

- One way to make equals stricter is to always treat objects of different classes as different.

- That could be achieved by modifying the equals methods in classes Point and
  ColoredPoint.

- In class Point, you could add an extra comparison that checks whether the run-time class
  of the other Point is exactly the same as this Point’s class, as follows:
Solution of Transitivity Problem:
class Point(val x: Int, val y: Int) {

    override def hashCode = 41 * (41 + x) + y           // Redefine hashCode
    override def equals(that: Any) = other match {      // Redefine equals()

         case that: Point => this.x == that.x && this.y == that.y
                           && this.getClass == that.getClass
         case _ => false
         }
}
    object Color extends Enumeration {
        val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value
    }

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
               extends Point(x, y) {

    override def equals(that: Any) = other match {
        case that: ColoredPoint =>(this.color == that.color)&& super.equals(that)
        case _ => false
    }
}
scala> redp == p                          res:0 Boolean : false
scala> p == bluep                         res:0 Boolean : false
scala> redp == bluep                      res:0 Boolean : false
- Here, an instance of class Point is considered to be equal to some other instance of the
  same class only if the objects have the same coordinates and they have the same run-
time class.

- Meaning .getClass on either object returns the same value.

- The new definitions satisfy symmetry and transitivity because now every comparison
  between objects of different classes yields false.

- So a colored point can never be equal to a point.
Object Equality in Scala

Object Equality in Scala

  • 1.
    Object Equality InScala Ruchi Agarwal Software Consultant Knoldus
  • 2.
    Object Equality InJava & Scala: - Two equality comparisons: 1. == Operator 2. Equals() - The == operator, which is the natural equality for value types and object indentity for Refernce types in Java. // This is Java boolean isEqual(int x, int y){ return x == y; // Natural equality on value types } System.out.println(isEqual ( 421, 421)); //Output: True //java.lang.Integer(Object) boolean isEqual(Integer x, Integer y){ return x == y; // Reference equality on reference types } System.out.println(isEqual ( 421, 421)); //Output: False
  • 3.
    - The ==equality is reserved in Scala for the ā€œ Natural ā€ equality of each type. The equality operation == in scala is designed to be transparent with respect to the type's representation. - For value types, it is the natural (numeric or boolean) equality. - For reference types, == is treated as an alias of the equals() inherited from object. //This is Scala scala> def isEqual(x : Int , y : Int) = x == y isEqual: (x: Int, y: Int )Boolean scala> isEqual( 421 , 421) res8: Boolean = true scala> def isEqual(x : Any , y : Any) = x == y isEqual: (x: Any , y: Any)Boolean scala> isEqual( 421 , 421) res8: Boolean = true
  • 4.
    String Comparisons: - Scala,never fall into Java's well-known trap concerning string comparisons. //This is Scala scala> val x=ā€œabcdā€.substring(2) x: java.lang.String = cd scala> val y=ā€œabcdā€.substring(2) y: java.lang.String = cd scala> x == y res7: Boolean = true //This is Java boolean isEquals(String x, String y){ return x.substring(2) == y.substring(2); //return x.substring(2).equals(y.substring(2)); //Output: true } String s1="abcd"; String s2="abcd"; System.out.println(isEquals(s1, s2)); //Output: false
  • 5.
    Reference Equality: - Forrefernce equality, Scala's class AnyRef defines an additional eq method, which cannot be overridden and is implemented as reference equality(i. e., it behaves like == in Java for reference types). scala> val x=new String(ā€œabcā€) x: java.lang.String = abc scala> val x=new String("abc") x: java.lang.String = abc scala> x == y res13: Boolean = true // Value equality scala> x eq y res14: Boolean = false // Reference Equality - There's also the negation of eq , which is called ne. scala> x ne y res14: Boolean = true
  • 6.
    - For refernceequality, the behavior of == for new types can redefine by overriding the equals method, which is always inherited from class Any. - It is not possible to override == directly, as it is defined as a final method in class Any. // defination of == in Any class final def == (that : Any) : Boolean = if ( null eq this) { null eq that } else { this equals that }
  • 7.
    Writing an EqualityMethod: - Writing a correct equality method is surprisingly difficult in object-oriented languages. - This is problematic, because equality is at the basis of many other things. - Here are four common pitfalls that can cause inconsistent behavior when overriding equals: 1. Defining equals with wrong signature 2. Changing equals without also changing hashCode 3. Defining equals in terms of mutable fields 4. Failing to define equals as an equivalence relation
  • 8.
    Pitfall #1: Definingequals with wrong signature class Point ( val x:Int, val y :Int) { def equals (other: Point): Boolean = //Overloaded equals() this.x == other.x && this.y == other.y } scala> val p1, p2 = new Point (1 , 2) p1: Point = Point@1e0bb90 p2: Point = Point@139fb49 scala> val q= new Point( 2 , 3) q: Point = Point@a44ec3 //Working OK scala> p1 equals p2 res:0 Boolean : true scala> p1 equals q res:0 Boolean : false //Trouble scala> val p2a: Any = p2 // Alias of p2 p2a: Any = Point@139fb49 // p2a calling equals() of Any class scala> p1 equals p2a res:0 Boolean : false
  • 9.
    Solution: Correct Signature class Point ( val x:Int, val y :Int) { // A better definition:Overriding equals of Any class override def equals(other: Any) = other match { case that: Point => this.x == that.x && this.y == that.y case _ => false } } scala> val p2a: Any = p2 // Alias of p2 p2a: Any = Point@139fb49 scala> p1 equals p2a res:0 Boolean : true
  • 10.
    - A relatedpitfall is to define == with a wrong signature. - If try to redefine == with the correct signature, which takes an argument of type Any, the compiler will give an error because you try to override a final method of type Any. - However, newcomers to Scala sometimes make two errors at once: They try to override == and they give it the wrong signature. - For instance: def ==(other: Point): Boolean = // Don’t do this! (User-defined == method) - In that case, the user-defined == method is treated as an overloaded variant of the same-named method class Any, and the program compiles. - However, the behavior of the program would be just as dubious as if you had defined equals with the wrong signature.
  • 11.
    Pitfall #2: Changingequals without also changing hashCode //Previously defined equals method class Point ( val x:Int, val y :Int) { // A better definition:Overriding equals of Any class override def equals(other: Any) = other match { case that: Point => this.x == that.x && this.y == that.y case _ => false } } scala> import scala.collection.mutable._ import scala.collection.mutable._ scala> val p1, p2 = new Point (1 , 2) p1: Point = Point@1bef480 p2: Point = Point@1a6068c //storing p1 to collection scala> val coll = HashSet (p1) coll: scala.collection.mutable.Set[Point] = Set(Point@62d74e) scala> coll contains p2 res2: Boolean = false
  • 12.
    - In fact,this outcome is not 100% certain. We might also get true from the experiment. We can try with some other points with coordinates 1 and 2. Eventually, we’ll get one which is not contained in the set. hashSet(p1) contains p2 val p2 true val p1 val p2 false hashSet Hash Bucket Hash Bucket Pitfall #2: Changing equals without also changing hashCode
  • 13.
    - Wrong hereis that Point redefined equals without also redefining hashCode. - The problem was that the last implementation of Point violated the contract on hashCode as defined for class Any: If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result. - In fact, it’s well known in Java that hashCode and equals should always be redefined together. - Furthermore, hashCode may only depend on fields that equals depends on. - For the Point class, the following would be a suitable definition of hashCode: override def hashCode = 41 * (41 + x) + y // Redefine hashCode - This is just one of many possible implementations of hashCode. - Adding hashCode fixes the problems of equality when defining classes like Point.
  • 14.
    class Point(val x:Int, val y: Int) { override def hashCode = 41 * (41 + x) + y // Redefine hashCode override def equals(other: Any) = other match { // Redefine equals() case that: Point => this.x == that.x && this.y == that.y case _ => false } } scala> val p1, p2 = new Point(1, 2) p1: Point = Point@6bc p2: Point = Point@6bc scala> coll contains p2 res2: Boolean = true
  • 15.
    Pitfall #3: Definingequals in terms of mutable fields: - Consider the following slight variation of class Point: class Point2(var x: Int, var y: Int) { override def hashCode = 41 * (41 + x) + y // Redefine hashCode override def equals(other: Any) = other match { // Redefine equals() case that: Point2 => this.x == that.x && this.y == that.y case _ => false } } scala> val p = new Point(1, 2) p: Point = Point@2b scala> val coll = HashSet(p) coll: scala.collection.mutable.Set[Point] = Set(Point@2b) scala> coll contains p res5: Boolean = true scala> p.x += 1 // Changing p.x value scala> coll contains p res7: Boolean = false scala> coll.elements contains p res7: Boolean = true
  • 16.
    coll.elements contains p true p.x += 1 After change value it (x:mutable) assign to wrong hash bucket p.x=1 p.x=2 coll contains p coll false Hash Bucket Hash Bucket Pitfall #3: Defining equals in terms of mutable fields - In a manner of speaking, the point p ā€œdropped out of sightā€ in the set coll even though it still belonged to its elements. - If you need a comparison that takes the current state of an object into account, you should usually name it something else, not equals.
  • 17.
    Pitfall #4: Failingto define equals as an equivalence relation: - In scala, Any class specifies that equals must implement an equivalence relation on non-null objects: • It is reflexive: for any non-null value x , the expression x.equals(x) should return true. • It is symmetric: for any non-null values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. • It is transitive: for any non-null values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true. • It is consistent: for any non-null values x and y, multiple invocations of x.equals(y) should consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified. • For any non-null value x, x.equals(null) should return false. - The definition of equals developed so far for class Point satisfies the contract for equals. - However, things become more complicated once subclasses are considered.
  • 18.
    Symmetric Problem: class Point(val x: Int, val y: Int) { override def hashCode = 41 * (41 + x) + y // Redefine hashCode override def equals(other: Any) = other match { // Redefine equals() case that: Point => this.x == that.x && this.y == that.y case _ => false } } object Color extends Enumeration { val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value } class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { // Problem: equals not symmetric override def equals(other: Any) = other match { case that: ColoredPoint =>this.color == that.color && super.equals(that) case _ => false } } scala> val p = new Point(1, 2) p: Point = Point@2b scala> val cp = new ColoredPoint(1, 2, Color.Red) cp: ColoredPoint =ColoredPoint@2b scala> p equals cp res:0 Boolean : true scala> cp equals p res:0 Boolean : false
  • 19.
    Solution of SymmetricProblem but violated Transitivity contract: class Point(val x: Int, val y: Int) { override def hashCode = 41 * (41 + x) + y // Redefine hashCode override def equals(other: Any) = other match { // Redefine equals() case that: Point => this.x == that.x && this.y == that.y case _ => false } } object Color extends Enumeration { val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value } class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { // Problem: equals not transitive override def equals(other: Any) = other match { case that: ColoredPoint =>this.color == that.color && super.equals(that) case that:Point=>that equals this //new case to make symmetric case _ => false } } scala> p equals cp res:0 Boolean : true scala> cp equals p res:0 Boolean : true
  • 20.
    - Here’s asequence of statements that demonstrates transitive. Define a point and two colored points of different colors, all at the same position: scala> var redp=new ColoredPoint(1,2,Color.Red) redp: ColoredPoint = ColoredPoint@6bc scala> var bluep=new ColoredPoint(1,2,Color.Blue) bluep: ColoredPoint = ColoredPoint@6bc scala> var p=new Point(1,2) p: Point = Point@6bc scala> redp == p res1: Boolean = true scala> p == bluep res1: Boolean = true scala> redp == bluep res1: Boolean = false - Hence, the transitivity clause of equals’s contract is violated. - One way to make equals stricter is to always treat objects of different classes as different. - That could be achieved by modifying the equals methods in classes Point and ColoredPoint. - In class Point, you could add an extra comparison that checks whether the run-time class of the other Point is exactly the same as this Point’s class, as follows:
  • 21.
    Solution of TransitivityProblem: class Point(val x: Int, val y: Int) { override def hashCode = 41 * (41 + x) + y // Redefine hashCode override def equals(that: Any) = other match { // Redefine equals() case that: Point => this.x == that.x && this.y == that.y && this.getClass == that.getClass case _ => false } } object Color extends Enumeration { val Red, Orange, Yellow, Green, Blue, Indigo, Violet = Value } class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { override def equals(that: Any) = other match { case that: ColoredPoint =>(this.color == that.color)&& super.equals(that) case _ => false } } scala> redp == p res:0 Boolean : false scala> p == bluep res:0 Boolean : false scala> redp == bluep res:0 Boolean : false
  • 22.
    - Here, aninstance of class Point is considered to be equal to some other instance of the same class only if the objects have the same coordinates and they have the same run- time class. - Meaning .getClass on either object returns the same value. - The new definitions satisfy symmetry and transitivity because now every comparison between objects of different classes yields false. - So a colored point can never be equal to a point.