views:

422

answers:

5

The following code when run obviously prints out "B1/A2/B2". Now, is it possible for it to print "A1/A2/B2" instead (i.e. A#method2() should invoke method1() on A, not on B)?

Note: I have no such need to get pass polymorphism, this question is out of curiosity only.

class A {
    public void method1() {
        System.out.println("A1");
    }

    public void method2() {
        method1();
        System.out.println("A2");
    }
}

class B extends A {
    @Override public void method2() {
        super.method2();
        System.out.println("B2");

    }

    @Override public void method1() {
        System.out.println("B1");
    }
}

public class Tmp {
    public static void main(String args[]) {
        B b = new B();
        b.method2();
    }
}
+4  A: 

I don't believe so; not if you're overriding method1() in a subclass. If you really required that behavior, you'd have to declare A.method1() as final, and you couldn't define it in B.

It doesn't make sense to me to do this - you should reconsider your design if you think you need to!

harto
+1  A: 

You could also make the A.method1 private. Then, even if you have B.method1, A.method1 will be called instead. But I'm not sure about that, you should check

artemb
+1  A: 

Yes, you can do it. Define A in package a:

package a;
public class A {
    void method1() {
        System.out.println("A1");
    }
    public void method2() {
        method1();
        System.out.println("A2");
    }
}

Define B in package b:

package b;
import a.A;
public class B extends A {
    @Override public void method2() {
        super.method2();
        System.out.println("B2");
    }
    void method1() {
        System.out.println("B1");
    }
}

Put your test in packae a and run it. The result is A1/A2/B2. Of course this is unhealthy: note the necessary omission of @Override on method1 - if you put it back in, you will get a compiler error:

method does not override or implement a method from a supertype
Yardena
this is wrong. to my understanding of java once you can no longer access overriddem methods once you instantiate a subclass.
Andreas Petersson
Andreas - your understanding is wrong. Package protection kicks in here and messes things up, compile and run to see for yourself. Here are couple of links that explain similar behavior: http://gbracha.blogspot.com/2009/03/subsuming-packages-and-other-stories.htmlhttp://dow.ngra.de/2009/02/16/the-ultimate-java-puzzler/
Yardena
+1  A: 

To do this, you'd have to method method1 non-virtual. To do that, you make it final:

  public final void method1() {....

Or you make it private; in Java pivate methods are always non-virtual:

   private void method1() {....

(Note that in C++, private methods may be virtual or non-virtual; among other things that makes implementing the Template Method Pattern cleaner in C++.)

Here's what's happening: when a non-static method is called through an object reference (which is the only way to call a non-static object method in Java), the method called depends on the actual type of the object referred-to (pointed-to), not the type of the reference.

Within an object method, calls to other methods (or members) of that object are implicitly prefixed with "this.". So your call to method1 in method2 is really:

public void method2() {
    this.method1();
    System.out.println("A2");
}

And this is an object reference. In method2, which is in class A, the reference is of type A, as if this had been declared:

A this;

But that reference points to an object of type B; it can do that because B is derived from, inherits, subclasses, is-a, A.

As I mentioned above, "when a non-static method is called through an object reference the method called depends on the actual type of the object referred-to, not the type of the reference." When you instantiated an object of type B, and called method2(), the this you passed in (this is really a hidden parameter of any non-static function) is a this that points to a B object. When B.method2() called super(), that same this was passed to A.method2().

And so when A.method2() called method1(), what really happened is we called this.method1() with that same this, which refers to a B, the B you instantied in main() and called method2() on.

Since method1() is virtual, and since we are calling method1() on a reference to an object of type B, the compiler makes sure that B's re-definition of method1() is the one called.

tpdi
A: 

Using standard "natural" and well accepted Java mechanisms you cannot. Java is designed so that all methods are dynamically dispatched unless final or static (or called to super), and in your case. Since the method is overridden in B, none of these is an option. There is no mechanism to "disable" dynamic dispatching in specific cases but the good news is that there is rarely a need to.

You could overcome some of these limitations with reflection, but that's like unwrapping a precious gift with sledgehammer.

Uri
Good point. As to the "reflection" part - I tried but it didn't work (http://stackoverflow.com/questions/784542/force-invocation-of-base-class-method/786042#786042)
Buu Nguyen
reflection is not going to help, but reducing visibility of method1 can, as I showed below.
Yardena
That's a good point. I treat reflection as an important mechanism that can be abused. I think if one was really inclined there might be a way to do this but I'm not sure, so I agree with Yardena.
Uri