1
public class A {
 int method(A a) { return 1; }
 static int method(B b) { return 2 + b.method((A)b); }
}

public class B extends A {
 final int method(A a) { return 4 + super.method(a); }
 int method(C c) { return 8 + this.method((B)c); }
}

public class C extends B {
 int method(C c) { return 16 + method((A)c); }
} 

public class Test {
    public static void main(String[] args) {

    A a = new A();
    B b = new B();
    C c = new C();

    b.method(b);  //returns 7 (4 + 2 + 1)
    b.method(c);  //returns 15 (8 + 4 + 2 + 1)
}

}

Each class is in its own file.

Why did the first call return 7? For me it should return something else because B.method(A) is invoked and then A.(B) and then something in B should be called and not A.(A) And why did the second call return 15?

pavelsaman
  • 7,399
  • 1
  • 14
  • 32

2 Answers2

2

There are two valid methods that can be called for b.method(b);:

final int method(A a) { return 4 + super.method(a); }
static int method(B b) { return 2 + b.method((A)b); } // inherited from A

The compiler chooses the more specific one - method(B). It doesn't matter that it is static. Static methods can be called on an instance too. See also: Is calling static methods via an object "bad form"? Why?

Now we need to resolve b.method((A)b);. b's compile time type is A so the compiler only searches in A. The argument expression's compile type is also A. The only applicable method is:

int method(A a) { return 1; }

At runtime, b is found to be an instance of B, so the overridden method in B is called instead:

final int method(A a) { return 4 + super.method(a); }

Now we resolve super.method(a);. The types involved here are exactly the same as those in b.method((A)b);, but we are using super, so the implementation in the superclass of B, i.e. in A, is called instead:

int method(A a) { return 1; }

For b.method(c);, the argument's compile time type is C, so we have three applicable methods:

final int method(A a) { return 4 + super.method(a); }
static int method(B b) { return 2 + b.method((A)b); } // inherited from A
int method(C c) { return 8 + this.method((B)c); }

Again, the most specific, method(C) is chosen.

Then we resolve this.method((B)c);. This is resolved in exactly the same way as b.method(b);. The compile-time types are the same. The fact that the runtime type of the parameter is a C object doesn't really matter, because all C overrides is the method(C) method, which we never called when resolving b.method(b);.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
2

For B valid functions are the following.

final int method(A a) { return 4 + super.method(a); }
static int method(B b) { return 2 + b.method((A)b); }
int method(C c) { return 8 + this.method((B)c); }
  • The overwritten method(A a) from class B
  • Also in reasons of overloading the static int method(B b)

Notice here static methods can also called on instances.

Now when method will be called because method is overloaded for multiple parameters it looks in which method the argument is applicable.

The function call stack for this b.method(b); //returns 7 (4 + 2 + 1)

is

  1. calls from class B method(B b) returns 2 + b.method((A)b)
  2. calls in class B method(A a) returns 4 + super.method(a) // because of super it calls not the overwritten method(A a) it calls the original
  3. calls in class A static int method(A a) returns 1

1 + 4 + 2 -> 7

for b.method(c)

  1. calls class B int method(C c) returns 8 + this.method((B)c);
  2. calls class B method static int method(B b) returns 2 + b.method((A)b);
  3. calls class B final int method(A a) returns 4 + super.method(a); // Calls the not overwritten method(A a) because super keyword
  4. calls class A int method(A a) returns 1

1 + 4 + 2 + 8 -> 15

Aalexander
  • 4,987
  • 3
  • 11
  • 34