4

I am wondering why the call of

z.f(-6);

in class M refers to the following function in class B:

public void f(double y) {
    this.x = (int) y + B.y;
}

instead of using function f in class A, since b.x is covered by A. Or rather uses

public void f (int y) {
    this.x = y*2;
    B.y = this.x;
}

in class B where at least the parameter type matches.

Complete Code below:

public class A {
    public int x = 1;
    public A(int x) {
        this.x += x;
    }
     public A (double x) {
        x += x;
    }
    public void f(double x) {
        this.x = this.x + (int) (x + B.y);
    }
}

public class B extends A {
    public static int y = 3;
    public int x = 0;
    public B (double x) {
        super((int) x);
    }
    public void f(int y) {
        this.x = y*2;
        B.y = this.x;
    }
    public void f(double y) {
        this.x = (int) y + B.y;
    }
  }

public class M {
      public static void main (String[] args){
        A a = new A(B.y);
        a.f(1);
        B b = new B(3.0);
        A z = b;
        z.f(-5.0);
        z.f(-6);
        System.out.println(b.x + "   " + z.x);
       }
  }
5
  • I think Mike Samuel's answer covered it pretty well, since z has type A, even though it holds an instance of A's subclass B, only methods of A can and will be invoked; for invocations of methods of subtype B, you would have to downcast z (always use checked downcasts though, for example using instanceof, because you might get runtime exceptions otherwise). - On an unrelated note, since I saw your profile on Theoretical Computer Science and am considering doing my master's at RWTH, how's studying there? Sounds somewhat demanding judging from your question on TCS? Commented Mar 13, 2012 at 23:51
  • EDIT: I misread Mike Samuel's answer, in his first sentence it needs to read "A.f(double)". A correct and less detailed answer (that's also less demanding while still capturing the question) would be Miserable Variable's; also check the comments on that one. Commented Mar 14, 2012 at 0:33
  • @ G. Bach: I can highly recommend RWTH University. It's challenging, but fun. I am only in my first semester, so my experiences are quite limited ;-) Commented Mar 15, 2012 at 18:46
  • Thanks for your feedback :) Just out of curiosity, your question about Eppstein's Algorithm (on TSC) was somewhat extracurricular I expect? I'm nearly done with my bachelor's and haven't had to look at anything besides course material (certainly not published articles) until now for doing my bachelor's thesis. Was that paper part of your coursework or was it more of a "I'd like to know that in more detail, lemme look that one up" thing? Commented Mar 15, 2012 at 19:01
  • @G.Bach: That was part of a proseminar on "Algorithms and Datastructures". Where are you studying? Maybe we should switch to email? Commented Mar 18, 2012 at 12:25

4 Answers 4

3
z.f(-6);

Static type of z is A, which has only one method named f. That method takes double parameter, to which the literal value -6 can be promoted. So at compile time the call is bound to A.f(double).

At runtime z is found to be of type B, which overrides A.f(double) with its own B.f(double), and so that this the method that gets called.

Sign up to request clarification or add additional context in comments.

3 Comments

I was under the impression that the call only binds to the subclass-method if the variable is cast to the subclass - a simple test however showed that you are right on this, even though at compile-time the call binds to A.f(double), at runtime it binds to B.f(double) since z is an instance of type B.
@G.Bach yes, that is the whole point of overriding, to determine the method to call depending on the dynamic type.
Do I understand this correctly then, that which method of an overloaded method name is bound to is decided at compile time, while the question of which class's overriding of that (then already selected) method is decided at runtime? What I tried was: if one adds a method A.f(int), the call z.(-6) will end up in B.f(int), so what to me seems to happen for that call is that at compile-time, the compiler chooses A.f(int) since z is in a variable of type A, while at runtime this is overwritten by B.f(int). I was not aware of that.
3

The static type of z is A so z.f(-6) can only bind to a method in A, which in this case is A.f(int).

The language is designed this way so that

A z = new B(3.0);
z.f(-6);

will always behave the same as

A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0);
z.f(-6);

If the compiler were to bind to a different method signature because it can prove that A z always holds a B then this would introduce all kinds of non-local effects to the language making it really hard to debug or maintain java programs.

Imagine someone trying to maintain

final A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0);
// 1000 lines elided
z.f(-6);

by changing it to

A z = new B(3.0);
// 1000 lines elided
z.f(-6);

If the compiler can now prove that A is always a B and binds Z.f to a method in B, the maintainer will be baffled.

7 Comments

The first sentence (and the main thrust) of this answer is completely incorrect. Of course you can invoke a method on a subclass when its type is referred to by the superclass - that's the essence of OOP!
@Bohemian, Try this program. class A {} class B extends A { void f() {} public static void main(String[] args) { A x = new B(); A.foo(); } }. You will get a compile error: "Cannot find symbol method foo()".
You are both right, depending on the situation. If you override a method in the subclass, then clearly it will be bound to even when referring to instances of the parent class, if you overload or create a new method, not so much.
@increment1, Would you agree with this following phrasing? At compile time, Java chooses the signature of an overloaded method based on the static types of the receiver and arguments, but among overridden methods with the same signature, all dispatch in java is virtual hence the runtime concrete type is used
@increment1, Would you also agree that the answer to this question hinges on a choice between different overloadings, not different overridings?
|
1

Java is single dispatch, whereas what you are attempting to do is double dispatch (where the method invoked depends on both the dynamic runtime class and parameters).

The signature of a method to invoke in Java is determined at compile time; this means that the declared class of the object determines which method is bound to. Overriding a method in a subclass affects the bound implementation, but overloading a method does not (since the overloaded method has a different signature).

In class B you are overloading f() with a version that takes an int, when using an object declared as being of class A, this method does not appear to exist (you cannot call it, and it will not be invoked).

To summarize:

  • The compiler binds to a method signature at compile time.
  • This bound signature depends on the declared (compile time) type of the object and parameters.
  • If a method is overridden in a subclass, then when invoking on the subclass (regardless of the declared type), the overridden method will be chosen.
  • Overloading is not overriding, to override you need the same method signature (well, excepting covariance).

Comments

0

Well I might be wrong, but if you link the object z of type A to an object of type B, it will still bind as type B, that's why it's executing the method in class B and not in class A. Note that you are NOT creating an object of type Asince you're not using new.

And as Mike Samuel said, by default -6 should be considered an int, but that doesn't fit with your explanation. I'll try to find out a proper answer for this.

1 Comment

Not "by default." In Java, -6 is always an int literal. Java does not have arbitrary precision constants like Go where "Numeric constants represent values of arbitrary precision and do not overflow."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.