Inheritance in Java, apparent type vs actual type

5.4k Views Asked by At

Can anyone explain me the result of this execution? I can't notice why each method is called. And how we distinguish the real type vs the apparent type. Thank you :)

public class JavaApplication15 {
   public static void main(String[] args) {
       A a = new A();
       B b= new B();
       A ab = new B();
       b.f(a);
       b.f(b);
       b.f(ab);
  }  
}

public class A {
   private final String id="A";
   public void f(A x){
     System.out.println("Send a fax to "+ x ) ;
  }
  public String toString(){return id;}
}

public class B extends A{
   private final String id="B";
   public void f(B x){
     System.out.println("Send an email to"+ x ) ;
   }
   public String toString(){return id;}
}

Result:

Send a fax to A
Send an email to B
Send a fax to B
4

There are 4 best solutions below

0
On

You are overloading which (per the wikipedia) creates multiple methods of the same name with different implementations. I think you meant to override the f(A x) method with the f(B x) method. Overriding (per the wikipedia) allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes.

Based on the surprise expressed in your question, I think you wanted something like

@Override
public void f(A x) {
  System.out.println("Send an email to"+ x ) ;
}
0
On
@Override
public String toString()
{
    return id;
}

This defines how the class is outputted. When you call a function such as the following:

b.f(a)

You are inputting a class as the parameter. Because of your toString() method, which is an override of the standard method to output your class, you display the id after Send an email to.

0
On

Since B extends A and A has a public method. So it has both of the following methods -

public void f(A x){
     System.out.println("Send a fax to "+ x ) ;
} 

And -

public void f(B x){
     System.out.println("Send an email to"+ x ) ;
}

Actually the seond method in B is an overloaded version of method void f(A x) inherited from A

0
On

An object of class B has both f(A) and f(B). When you call f(x), it generally chooses the most specific one that matches the type of x. For this, what matters is the type of the reference.

So your a is an A, therefore it calls f(A). Your b is a B, therefore it calls f(B). But what is your ab? The type of the reference is A. So the compiler tells it to call the f(A) overload.

But then, shouldn't it have used the ab object as an A and therefore printed send a fax to A using A.toString() rather than B.toString()?

No, because the choice of method to be called in this case is not determined by the parameter. It's not a choice between overloads as the above choice was, it is a choice between overrides. When deciding which method to use, the one from the superclass or the one from the subclass, the actual type of the object determines it. Since the object referred to by ab is in fact a B, the toString() of B is chosen.

So - among overloaded methods, the one that is chosen is determined based on the number and type of the argument, and the the type of the argument is the type of the reference.

Among overridden methods, the one that is chosen is the one in the actual type of the object instance, regardless of what the type of the reference through which it is invoked.

Note that a method is only considered overridden if it has the same name, number of parameters and respective types of parameters as the parent method.