Robert
Robert

Reputation: 10963

Calling overloaded inherited methods using super class reference

I do not understand this Java behavior. I have two classes:

class C1 {
    public void m1(double num) {
        System.out.println("Inside C1.m1(): " + num);
    }
}

class C2 extends C1 {
    public void m1(int num) {
        System.out.println("Inside C2.m1(): " + num);
    }
}

And it is my main:

public class Main {

    public static void main(String[] args) {
        C1 c = new C2();
        c.m1(10);
    }
}

And the result was:

Inside C1.m1(): 10.0

When I expected:

Inside C2.m1(): 10

Also when I try to complete the code syntax, I found this:

Enter image description here

Where is the other m1 of C2 class?

I also check the bytecode of my Main.class and I saw this:

Compiled from "Main.java"
public class com.company.Main {
  public com.company.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/company/C2
       3: dup
       4: invokespecial #3                  // Method com/company/C2."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc2_w        #4                  // double 10.0d
      12: invokevirtual #6                  // Method com/company/C1.m1:(D)V
      15: return
}

The bytecode tell me that it will invoke the C1.m1 (D)V (line 12).

Why the method of C1? I am trying to understand this behavior.

Upvotes: 17

Views: 4350

Answers (10)

Swadeshi
Swadeshi

Reputation: 1672

Java chooses the most specific applicable type. In this case m1(int) is not applicable. Emphasis on the reference of class that hold object of same class(c1) or an object of any sub classes of class(c2) & method name and parameter list .

Your method with double parameters is being called because double takes priority over int. This is because an int can be assigned to a double, but not the other way around.

So there are so many things to be consider at the (run) time of method call.

Yes for ur case your main class should be like this

        public static void main(String[] args) {
            C1 c = new C2();
            c.m1(10);
            ((C2) c).m1(10);
    //or
            C2 cobj = new C2();
            cobj.m1(10);
        }


**OutPut**
Inside C1.m1(): 10.0
Inside C2.m1(): 10
Inside C2.m1(): 10

Upvotes: 0

George Birbilis
George Birbilis

Reputation: 2940

If you call c.m1(10.0) it will call the method of the ancestor as you were expecting at first.

You're doing method overloading in your example (that is adding more methods with the same name and different signature) instead of method overriding (that is changing the implementation of an ancestor's method at a descendent by redeclaring it with the same signature, AKA same name and same type of result and of method arguments - argument names shouldn't matter).

Upvotes: 3

loverBoy
loverBoy

Reputation: 125

By looking at your code, you are not taking advantage of inheriting to get the answer you want. You have to change this line

C1 c = new C2();

to

C2 c = new C2();

Upvotes: 2

Aniket Thakur
Aniket Thakur

Reputation: 69035

Method signatures for both methods are different.

public void m1(double num) 
public void m1(int num)

So there is no overriding in this case. Now when you say

    C1 c = new C2();
    c.m1(10);

at compile time, it will se reference is of type C1 which has method public void m1(double num) which is compatible with 10 [int in expanded to double]. So int is promoted to double, and the corresponding method is called (which is also what you see in the bytecodes).

Upvotes: 4

Razib
Razib

Reputation: 11173

Since C1.m1(double num) is a public method, it inherited C2. So your C2 also has a method, m1(double num), and that's why it is called. From main() you actually called C2.m1(double num).

Note: Now at class C2 you have two overloaded methods - m1(int num) and m1(double num). And C2.m1(int num) is a different method from C2.m1(double num).

Upvotes: 1

drRobertz
drRobertz

Reputation: 3638

Java does method dispatch on static types, and your variable c is of type C1, so m1(int) is not visible, and your 10 is cast to double.

Upvotes: 6

Distjubo
Distjubo

Reputation: 1009

It's because of the parameters. The method you call is a method with a double parameter. m1 inside of C2 is not overriding this, instead it's overLOADING it.

If you want to call m1 in C2, you have to cast the reference such that the compiler accepts what you're doing.

Upvotes: 9

Chetan Kinger
Chetan Kinger

Reputation: 15212

The reason why you see the output as Inside C1.m1(): 10.0 and not Inside C1.m1(): 10 or Inside C2.m1(): 10.0 is because :

  1. You are not overriding the method m1 in C2. You are overloading the m1(doube) method that you inherited from C1 to m1(int) instead.
  2. The C2 class now has two m1 methods. One that is inherited from C1 and has the signature m1(double) and one that is overloaded in C2 and has the signature m1(int)
  3. When the compiler sees the call c.m1(10), it resolves this call based on the reference type. Since the reference type is C1, the compiler is going to resolve this call to m1(double) in C1.
  4. At runtime, the JVM is going to resolve the call to m1(double) in C2 which is the method inherited from C1. (As explained in point 2)

There are two ways in which the m1(int) method can be called :

((C2)c).m1(10);

OR

C2 c = new C2(); c.m1(10);

Upvotes: 8

Dejan
Dejan

Reputation: 353

You cannot see methods of C2, because you instance variable is declared as C1 and also because they don't have the same signature. You have double parameter in one method and in second a int type which makes them for JVM completely different methods (so no inheritance will work here).

So if you have int type in C1 method then you need to have also int type in C2 method, then JVM will run method from C2 like you wanted.

Also you can cast variable to C2 type then you will be able to access to methods of C2.

Upvotes: 2

Your two methods named m1 do not have the same signature; the one in the superclass takes a double, and the one in the subclass takes an int. This means that the compiler will select the method signature to call based on the compile-time type of your variable, which is C1, and will call m1(double). Since at runtime the class C2 doesn't have an overriding version of m1(double), the version from C1 is invoked.

The rule is that method signatures are computed at compile time based on compile-time types; method calls are dispatched at runtime based on matching signatures.

Upvotes: 16

Related Questions