Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java polymorphism aliasing issue

If there's 3 classes. A, B and C. class B extends A and class C extends B.

class A has equals method:

public boolean equals(A other)
{...}

class B has equals method:

public boolean equals(B other)
{...}

and class C has euals method:

public boolean equals(Object other)
{...}

And the main has these code lines:

A a = new A();
C c = new C();
a=c;
System.out.println(a.equals(c));

I can't understand why the equals method of class A is being executed.

I know that overloaded methods are bonded using static binding. But a points to the "C part of the object" after aliasing and there's the method equals of class C. Why isn't it the equals method of class C that will execute?

like image 683
Art Avatar asked Mar 25 '17 04:03

Art


3 Answers

Short answer

Your method in child class gets overridden only when it have same method signature as that of parent class. Otherwise it doesn't.


Consider these two examples

Example 1

class A {
    public boolean equals(A Other) {
        System.out.println("A's method");
        return true;
    }
}

class C extends A {
    public boolean equals(Object Other) {
        System.out.println("C's method");
        return true;
    }
}

public class Driver {
    public static void main(String[] args) {
        A a = new C();
        a.equals(null);
    }
}

Output 1

A's method

Example 2

class A {
    public boolean equals(A Other) {
        System.out.println("A's method");
        return true;
    }
}

class C extends A {
    public boolean equals(A Other) {
        System.out.println("C's method");
        return true;
    }
}

public class Driver {
    public static void main(String[] args) {
        A a = new C();
        a.equals(null);
    }
}

Output 2

C's method

In example #1, you have changed the method signature when equals() method is defined in child class and so it doesn't qualify for method overriding. And so when calling this method from Driver class, parent's gets called.

whereas in example #2, the method signature in both parent and child are exactly the same and so method overriding takes place here and thus you get output that you were expecting in your question.

like image 32
Raman Sahasi Avatar answered Oct 13 '22 05:10

Raman Sahasi


In Simple words, java handles aliasing during runtime. as overloaded method use static binding(compile time), it is calling A's equals method. if you override equals method in class A.

@Override
public boolean equals(Object o){ ...}

it will be overridden method which uses dynamic binding(runtime) and C's equals method will be called.

like image 35
lucid Avatar answered Oct 13 '22 06:10

lucid


A method in a subclass overrides a method in the superclass only if the parameters have the same types.

The Object class defines an equals() method:

class Object {
    public boolean equals(Object obj) {...}
}

When you define class A, it inherits the equals routine from Object. You define a new equals, but the parameter type is different, so it doesn't override the one in Object; instead, it becomes an overload. The result is that A has two overloaded equals methods:

class A {
    public boolean equals(Object obj) {...}  // inherited
    public boolean equals(A other) {...}     // the one you wrote
}

Similarly, the equals in B won't override either equals, so the result is three overloaded equals methods:

class B {
    public boolean equals(Object obj) {...}  // inherited from Object
    public boolean equals(A other) {...}     // inherited from A
    public boolean equals(B other) {...}     // doesn't override anything
}

In class C, the new equals method does override the one in Object, so there are still three equals methods:

class C {
    public boolean equals(Object other) {...}  // overrides the one in Object
    public boolean equals(A other) {...}       // inherited from A
    public boolean equals(B other) {...}       // inherited from B
}

Now, here's your code:

A a = new A();
C c = new C();
a=c;
System.out.println(a.equals(c));

When you say a.equals(c), the compiler sees that a has type A. Therefore it looks at the methods in A to see which one to execute. (The compiler doesn't know that a will have type C at run time; therefore, it won't look at the methods in C.)

There are two methods to choose from, as shown above:

    public boolean equals(Object obj) {...}  // inherited
    public boolean equals(A other) {...}     // the one you wrote

Both of them could be used on the parameter c, since c is an Object and it is an A. In that case, when one parameter is a subclass of the other, the compiler chooses the "closest" one, in essence. C is only two subclasses away from A, and it's three subclasses away from Object, so it chooses the overload with parameter A, which is the one you defined in A. And note that this equals method was never overridden. So it executes the code that you wrote in class A.

But suppose you had written:

System.out.println(a.equals((Object)c));

By casting c to an Object, you're forcing the compiler to look at it as an Object. Now when it chooses between the overloads, it must choose the one with the Object parameter, because an Object cannot automatically be converted to an A (because not every Object is an A). Thus, it would choose the inherited method. And since, at run time, the object actually is of class C, and since class C has an equals method that overrides the one in Object, in this case it would execute the code written in class C.

Your code is a nice example for demonstrating how overloading and overriding work. In real life, however, it's a bad idea to write an equals() method whose parameter is anything other than Object, because it won't override and it could lead to confusion. Also, it's a good practice to put @Override on any method that you think will override a method in a superclass. That way, if you goof and use the wrong parameters, the compiler will catch it before you get a run-time bug that could be very difficult to track down.

like image 61
ajb Avatar answered Oct 13 '22 06:10

ajb