This tutorial explains how to override predefined methods like equals (), hashCode (), compareTo (), etc. in Java with examples:
In our previous tutorial, we discussed runtime polymorphism in Java. Runtime polymorphism in Java is implemented using method overriding. Method overriding involves redefining the parent class method in the subclass.
Java has various predefined methods like equals (), hashCode (), compareTo (), toString (), etc. that are used commonly for general objects irrespective of which class they belong to. But for these methods to work for all objects, we need to override these methods or redefine their implementations so that they can work with the data we want.
=> Visit Here To Learn Java From Scratch.
In this tutorial, we will discuss the overriding of all these methods along with the consequences if we do not override these methods.
Table of Contents:
Overriding equals () And hashCode () Methods In Java
We use the equals () method in Java to compare two objects. This method returns true when the objects are equal and false when not equal.
Two ways are used to compare the equality of two objects.
#1) Shallow Comparison
Shallow comparison is the default implementation for the equals () method defined in “java.lang.Object” class. As a part of this implementation, the equals () method will check if two objects being compared have references referring to the same object.
This means that if obj1 and obj2 are two objects, then the default implementation of the equals () method (shallow comparison) will only check if the references of obj1 and obj2 are from the same object.
In shallow comparison, no data contents are compared.
#2) Deep Comparison
In deep comparison, we compare the data members of each object i.e. the objects are compared with respect to the state. So we compare the objects at a deep level including its contents.
To compare the objects using deep comparison we usually override the equals () method.
Now consider the following Java program.
class Complex { private double r, i; //declare real and imaginary component as private public Complex(double r, double i) { //constructor this.r = r; this.i = i; } } public class Main { public static void main(String[] args) { Complex c1 = new Complex(5, 10); //c1 object Complex c2 = new Complex(5, 10); //c2 object if (c1 == c2) { System.out.println("Two Complex objects are Equal "); } else { System.out.println("Two Complex objects are not Equal "); } } }
Output:
If we see the output of the above program, it says that the objects are not equal even though the contents of the two objects are the same. This is because, when the equality is checked, it is determined as whether the two objects c1 and c2 refer to the same object.
As seen in the program c1 and c2 are two different objects, so they are different references and thereby is resulted so.
Now let’s create a third reference c3 and equate it to c1 as follows:
Complex c3 = c1;
In this case, c3 and c1 will refer to the same object, and hence (c3==c1) will return true.
What the above program did was the shallow comparison. So how do we check if two objects are the same content-wise?
Here, we go for a deep comparison, and for this purpose, we override the equals () method.
The following program shows the overriding of the equals () method. We use the same Complex class.
class Complex { private double r, i; public Complex(double r, double i) { this.r = r; this.i = i; } // override equals () method to compare two complex objects @Override public boolean equals(Object obj) { // returns true=>object is compared to itself if (obj == this) { return true; } //return false if obj is not an instance of Complex class if (!(obj instanceof Complex)) { return false; } // typecast obj to Complex type Complex c = (Complex) obj; // Compare the contents of two objects and return value return Double.compare(r, c.r) == 0 && Double.compare(i, c.i) == 0; } } public class Main { public static void main(String[] args) { Complex c1 = new Complex(5, 10); Complex c2 = new Complex(5, 10); if (c1.equals(c2)) { System.out.println("Complex objects c1 and c2 are Equal "); } else { System.out.println("Complex objects c1 and c2 are not Equal "); } } }
Output:
Now that we have an overridden equals () method, when we compare two objects, the output shows that the two objects are equal as their contents are the same. Note the overridden equals () method. Here we check if both the objects have the same reference. If not then we individually check the contents of these objects.
In Java, whenever we override the equals () method, it is advisable to override the hashCode () method as well. This is because if we do not override the hashCode() method, then each object may have different hashCode.
This may not interfere with the general objects but certain hash-based collections like HashTable, HashSet, and HashMap may not work properly.
The following program shows equals() and hashCode() methods overridden.
import java.io.*; import java.util.*; class EqualsHashCode { String name; int id; EqualsHashCode(String name, int id) { this.name = name; this.id = id; } @Override public boolean equals(Object obj) { //both objects refer to same object if(this == obj) return true; //obj is not an instanceof EqualsHashCode if(obj == null || obj.getClass()!= this.getClass()) return false; // type cast obj EqualsHashCode o_obj = (EqualsHashCode) obj; // compare the objects return (o_obj.name.equals(this.name) && o_obj.id == this.id); } @Override public int hashCode() { // return current object's id as hashCode return this.id; } } class Main { public static void main (String[] args) { // create two objects with same state EqualsHashCode e1 = new EqualsHashCode("Java", 1); EqualsHashCode e2 = new EqualsHashCode("Java", 1); //update the objects Map<EqualsHashCode, String> map = new HashMap<EqualsHashCode, String>(); map.put(e1, "C++"); map.put(e2, "Python"); //display contents for(EqualsHashCode eh : map.keySet()) { System.out.println(map.get(eh).toString()); } } }
Output:
In this program, we use a hashMap. We have overridden both equals () and hashCode () methods. So when we say map.put (e1, “C++”), it hashes to some bucket location. Next, we call a map.put (e2, “Python”). This time it will hash to the same bucket and replace the previous value. This is because we have overridden the hashCode () method.
Overriding Static Method In Java
Can we override the static method in Java?
As far as overriding the static method in Java is concerned, the direct reply to this question is No, we cannot override the static method.
The static method is invoked using the class name itself. We do not need an object to call a static method. So even if we declare a method with the same prototype in a subclass, we cannot call it overriding. Instead, we are just hiding the parent class definition of the static method.
The following Java program shows the static method and non-static method in an inheritance system along with their behavior at runtime.
class Parent { // Parent class static method cannot be overridden by Child public static void display() { System.out.println("Parent class::static display()"); } // parent class non-static print method to be overridden by Child public void print() { System.out.println("Parent class::non-static print()"); } } // Subclass class Child extends Parent { // static display() method =>hides display() in Parent class public static void display() { System.out.println("Child class:: static display()"); } //overrides print() in Parent class public void print() { System.out.println("Child class::Non-static print()"); } } public class Main { public static void main(String args[ ]) { Parent new_obj = new Child(); // static methods are call as per the reference type. Since reference type //Parent, this call will execute Parent class's display method new_obj.display(); // here the print () method of Child class is called new_obj.print(); } }
Output:
From the program output, we can conclude the following.
- The call to the static method is always made based on the type of reference. Hence when we called new_obj. display () in the above program, as the new_obj reference is of type class Parent, the display () method of Parent class is called.
- On the other hand, non-static methods are called based on the contents of the reference object with which the method is called. Hence in the above program new_obj.print () method calls the print () method of the child class as new_obj contents are the object of the child class.
This explains the output of the program above and we need to remember the following points as well while dealing with static methods in the OOP system.
- A static method cannot hide a non-static instance method and a non-static instance method cannot override a static method.
- We can overload the methods from the parent class in a subclass but they neither override nor hide the parent class methods, rather they are new methods in the subclass.
Overriding compareTo () In Java
We know that the interface java.lang.Comparable provides a ‘compareTo ()’ method using which we can sort the objects in a natural order like lexical order for String objects, numeric order for Integers, etc.
To implement sorting in user-defined objects or collections, we need to override the compareTo () method to sort the collection elements or user-defined objects.
So what a compareTo () method does?
A compareTo() method must return positive value if the current object is greater than the passed object in order and the negative value of the current object is lesser than the passed object. If both the objects are equal, then the compareTo () method will return zero.
Another point to note is that the method equals () and compareTo () should behave consistently with each other. This means that if the compareTo () method returns that two objects are equal (returns zero) then we should have the same output from the equals () method as well.
Let’s implement a Java program that overrides the compareTo () method. In this program, we are using a Color class that has two private variables i.e. name and id. We have associated the ‘id’ with each color and we will override the compare () method to arrange colors according to the id.
import java.util.*; //color class class Color implements Comparator<Color>, Comparable<Color> { private String name; private int id; Color() { } Color(String n, int id) { this.name = n; this.id = id; } public String getColorName() { return this.name; } public int getColorId() { return this.id; } // Overriding the compareTo method @Override public int compareTo(Color c) { return (this.name).compareTo(c.name); } // Overriding the compare method to sort the colors on id @Override public int compare(Color c, Color c1) { return c.id - c1.id; } } public class Main { public static void main(String args[]) { // List of Colors List<Color> list = new ArrayList<Color>(); list.add(new Color("Red", 3)); list.add(new Color("Green", 2)); list.add(new Color("Blue", 5)); list.add(new Color("Orange", 4)); list.add(new Color("Yellow", 1)); Collections.sort(list); // Sorts the array list System.out.println("The list of colors:"); for(Color c: list) // print the sorted list of colors System.out.print(c.getColorName() + ", "); // Sort the array list using comparator Collections.sort(list, new Color()); System.out.println(" "); System.out.println("The sorted list of colors:"); for(Color c: list) // print the sorted list of colors as per id System.out.print(c.getColorId() + ":" + c.getColorName() + " , "); }
Output:
In the above output, we first display the list of colors and then the sorted list of colors. In the program, we have overridden compareTo () and compare () methods.
Override toString () Method In Java
The method ‘toString ()’ returns the String representation of an object in Java. But when we have user-defined objects, then this method may behave differently.
For example, consider the following program.
class Complex { private double r, i; public Complex(double r, double i) { this.r = r; this.i = i; } } public class Main { public static void main(String[] args) { Complex c1 = new Complex(5, 20); //create complex class Object //print the contents of complex number System.out.println("Complex number contents: " + c1); } }
Output:
As shown in this program we are displaying the Complex class object which we have defined earlier. However, the output shown is not the contents but it’s rather cryptic.
The output shows a class name Complex followed by ‘@’ character and then the hashCode of the object. This is the default output printed by the toString () method of the Object class.
If we want the proper output then we need to override the toString ( ) method in our application.
The following Java program shows how to override the toString () method to print the contents of the Complex object.
class Complex { private double r, i; public Complex(double r, double i) { this.r = r; this.i = i; } //override toString () method to return String representation of complex number @Override public String toString() { return String.format(r + " + i " + i); } } public class Main { public static void main(String[] args) { Complex c1 = new Complex(10, 15); System.out.println("Complex Number contents: " + c1); } }
Output:
The above program shows that the toString () method is overridden to return the contents of Complex object in the given format (real + i*imaginary).
In general, when we want to display the class object using either print () or println () then it is always advisable to override the toString () method so that we get the proper output.
Frequently Asked Questions
Q #1) Why use .equals instead of == Java?
Answer: We use ‘==’ to compare primitive types like int, char, boolean, etc. We use equals () to compare objects (predefined or user-defined). We usually override the equals () method to compare two objects and the return value of the equals () depends on the overridden code.
Q #2) What is hashCode () and equals () used for?
Answer: In Java, the equals () method is used to compare the equality of two objects. The hashCode () method returns the hashCode of the object. While the equals () method is used with most objects to test their equality, the hashCode is mostly used in hash collections like HashTable, HashMap, HashSet, etc.
Q #3) Can we change the argument list of the overridden method?
Answer: No. When we override the method, we keep the method signature or prototype of the method the same in the subclass as well. So we cannot change the number of parameters in the overridden method.
Q #4) Why do we override toString ()?
Answer: When the toString () method is overridden, we can return the values of the object for which the toString () method is overridden without writing too much code. This is because the java compiler invokes the toString () method when we print an object.
Q #5) What would happen if you will not override the toString () method?
Answer: If we do not override the toString () method, then we will not get any information about the properties or state of the object. We will not know what is actually inside the object. So all the classes should override the toString () method.
This is because the default implementation of the toString () method displays the String representation but when we use default toString () implementation on the object then we will not get the contents of the object.
Conclusion
In this tutorial, we discussed on overriding a few predefined Java methods and we also saw why we need to override them.
When we deal with objects, the default implementations of methods like equals (), compareTo(), and toString () may not give the correct information. Hence we go for overriding.
=> Take A Look At The Java Beginners Guide Here.