Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Java determine what method to invoke by implicit type invokations?

This problem presented itself when me and my friends were studying for exams. We noticed strange behavior when calling a statically assigned variable's method.

Code>Words so here we go:

class C {

    public void a(double x) {}
}

class D extends C {
    void b() {} 
}

class F extends D {

    void a(int i) {} 
    void a(double d) {} 

Now, doing

D foo = new F();
foo.a(1);

What will this give? Well.. it runs method a(double) in F !!

This is what we thought happened:

  1. Program start by looking for a in static type D: nothing there.
  2. Goes to its super class, C. Does not find a(int) but instead finds a(double).
  3. Decides that this is the signature i meant (i.e. a(double)), but FIIRST, after all this search let's look at the dynamic type first it says!
  4. Runs a(double) in F!

Is this correct? This means that it climbed the hierarchy to find some method that could fit if type conversion from int to double is done.. AFTER this it checks to see if the dynamic type has this newly interpreted signature.

I noticed that if I added

void a(int) {}

** in class C, it would give me a(int) in F when running the invocation above!

Can someone please explain the mechanics involved in this process? Why is so that the code runs/compiles this way? What's the technical reasons behind this? And are there any more things one should be aware of concerning similar cirumstances ()

like image 240
tortal Avatar asked Dec 21 '22 12:12

tortal


2 Answers

The reason is because Java is statically typed. Dispatch on argument type is done at compile time, not runtime.

When the code is compiled, the compiler sees that you are invoking a method named a on an object of static type D. It looks for compatible methods in D, and finds a single method (inherited from C). It generates code to perform a virtual call to C.a(double).

At runtime, the virtual call dispatches on the actual type of the object (not the arguments!), so it ends up calling F.a(double), since this overrides C.a(double).

The fact that the runtime type of the object is F and F happens to have a different method that would have been valid if it were known at compile time is irrelevant. If you want this behavior, you need reflection.

If you added C.a(int), the compiler would see two different methods named a in D, and based on the overloading rules, choose the one that takes an int.

like image 121
Antimony Avatar answered May 09 '23 19:05

Antimony


The method to call is resolved at compile time, not runtime. Since the compiler only knows at compile time that foo is a D, and therefore only has a method a(double), that is the method which is called. Precisely which object's a(double) to call is done dynamically (at runtime) however, which is why F.a(double) will be called and not C.a(double).

This is called single dispatch: the method to call is dynamic on the object it is being invoked on, but static on its argument types.

If you removed that method in C entirely, it wouldn't call a(int) in F, it would just fail to compile saying that it can't find that method.

like image 42
Mark Peters Avatar answered May 09 '23 21:05

Mark Peters