Core Java coding questions frequently asked in job interviews - part 1
Core Java coding questions frequently asked in job interviews - part 2
This part of the Java coding interview question is on the popular method overloading versus overriding concept.
Q. What will be the output of the following code snippet?
public class MethodOverrideVsOverload {A. The output will be
public boolean equals( MethodOverrideVsOverload other ) {
System.out.println("MethodOverrideVsOverload equals method reached" );
return true;
}
public static void main(String[] args) {
Object o1 = new MethodOverrideVsOverload();
Object o2 = new MethodOverrideVsOverload();
MethodOverrideVsOverload o3 = new MethodOverrideVsOverload();
MethodOverrideVsOverload o4 = new MethodOverrideVsOverload();
if(o1.equals(o2)){
System.out.println("objects o1 and o2 are equal");
}
if(o3.equals(o4)){
System.out.println("objects o3 and o4 are equal");
}
}
}
MethodOverrideVsOverload equals method reached
objects o3 and o4 are equal
What concepts does this question try to test?
- In Java, a class can only extend a single class (i.e. single inheritance), and when it does not explicitly extend a class, it implicitly extends the class Object. So, MethodOverrideVsOverload implicitly extends the class Object.
- The majority of the non final Object class methods are meant to be overridden by the sub classes.
public boolean equals(Object obj); // make note of this method
public int hashCode();
public String toString(); - The method overloading takes place at compile time (i.e. static binding) and method overriding takes place at runtime (i.e. dynamic binding). Static binding means the JVM decides, which class or method to call during compile time. Dynamic binding means, the JVM decides, which class or method to call during runtime. The polymorphism is possible because of dynamic binding. Learn more about compile-time vs runtime.
- The method overriding must adhere to the following rules
Arguments Must not change. Return type Can't change except for covariant (subtype) returns. Exceptions The extending class can eliminate or call fewer exceptions than its parent, but must not throw new or broader checked exceptions. Access Must not be more restrictive than the class it extends. Can be less restrictive. Invocation Which method to call is based on object type, at runtime time (i.e. dynamic binding).
Now, if you look at the above code
The "equals(MethodOverrideVsOverload other)" method in class MethodOverrideVsOverload does not actually override the Object class's "public boolean equals(Object obj)" method. This is because it fails to adhere to the "Arguments" rule as both methods have different arguments as one is of type "MethodOverrideVsOverload " and the other of type "Object" . So, the two methods are overloaded (happens at compile time) and not overridden.
So, when o1.equals(o2) is invoked, the public boolean equals(Object obj) method of the object class is invoked because during compile time, the o1 and o2 are of type Object.The Object class's equals( ... ) method returns false as it compares the memory address (e.g. Object@235f56 and Object@653af32) of both objects.
When o3.equals(o4) is invoked, the "equals( MethodOverrideVsOverload other )" of the class MethodOverrideVsOverload is invoked as during compile time o3 and o4 are of type MethodOverrideVsOverload, hence you get the above output.
What follow on questions can you expect?
Q. How will you fix the above issue?
A. In Java 5, annotations were introduced and one of the handy compile time annotations is the @override, which will ensure that the methods are overridden correctly. If you had this annotation, when you override it incorrectly as in the above example, a compile time error will be thrown.
So, to fix it, add the @override annotation to the "boolean equals( MethodOverrideVsOverload other )" of the MethodOverrideVsOverload class. This will give you a compile time error indicating that the method is not properly overridden. Now, fix the method signature by changing the argument type in the method signature from "MethodOverrideVsOverload" to "Object" as shown below.
So, when o1.equals(o2) is invoked, the public boolean equals(Object obj) method of the object class is invoked because during compile time, the o1 and o2 are of type Object.The Object class's equals( ... ) method returns false as it compares the memory address (e.g. Object@235f56 and Object@653af32) of both objects.
When o3.equals(o4) is invoked, the "equals( MethodOverrideVsOverload other )" of the class MethodOverrideVsOverload is invoked as during compile time o3 and o4 are of type MethodOverrideVsOverload, hence you get the above output.
What follow on questions can you expect?
Q. How will you fix the above issue?
A. In Java 5, annotations were introduced and one of the handy compile time annotations is the @override, which will ensure that the methods are overridden correctly. If you had this annotation, when you override it incorrectly as in the above example, a compile time error will be thrown.
So, to fix it, add the @override annotation to the "boolean equals( MethodOverrideVsOverload other )" of the MethodOverrideVsOverload class. This will give you a compile time error indicating that the method is not properly overridden. Now, fix the method signature by changing the argument type in the method signature from "MethodOverrideVsOverload" to "Object" as shown below.
public class MethodOverrideVsOverload {
@Override
public boolean equals( Object other ) {
System.out.println("MethodOverrideVsOverload equals method reached" );
return true;
}
public static void main(String[] args) {
Object o1 = new MethodOverrideVsOverload(); //during compile time o1 is of type Object
//during runtime o1 is of type MethodOverrideVsOverload
Object o2 = new MethodOverrideVsOverload(); //during compile time o2 is of type Object
//during runtime o2 is of type MethodOverrideVsOverload
MethodOverrideVsOverload o3 = new MethodOverrideVsOverload(); //o3 is of type MethodOverrideVsOverload
// during both compile time and runtime
MethodOverrideVsOverload o4 = new MethodOverrideVsOverload(); //o4 is of type MethodOverrideVsOverload
// during both compile time and runtime
if(o1.equals(o2)){
System.out.println("objects o1 and o2 are equal");
}
if(o3.equals(o4)){
System.out.println("objects o3 and o4 are equal");
}
}
}
The output will be
MethodOverrideVsOverload equals method reached
objects o1 and o2 are equal
MethodOverrideVsOverload equals method reached
objects o3 and o4 are equal
This is because now the methods are overridden and this happens at runtime. This is a bit tricky question, and think out loud at the interview to show that you understand the fundamentals.