views:

188

answers:

6

I'm new to Java, and I've read over some tutorials on overriding methods, but an example I'm looking at isn't working the way I expect. For example, I have the code:

public class A{
    public void show(){
        System.out.println("A");
    }
    public void run(){
        show();
    }
    public static void main( String[] arg ) {
        new A().run();
    }
}
public class B extends A{
    @Override
    public void show(){
        System.out.println("B");
    }
}

When I instantiate and call B.run(), I would expect to see "B" outputted. However, I see "A" instead. What am I doing wrong?

Edit: Yes, the classes are in two separate files. They're shown together for brevity.

Edit: I'm not sure how B is being instantiated, as it's being done by a third-party program using a classloader.

Edit: More info on the third-party program. It starts by calling A.main(), which I didn't initially show (sorry). I'm assuming I need to make "new A().run();" more generic to use the name of the current class. Is that possible?

+3  A: 

I tried, putting your two classes in two files, and it worked nicely, outputting "B". I called :

 B b = new B();
 b.run();

UPDATED : Also works as (because it is the same runtime instance):

 A a = new B();
 a.run();
KLE
`A a = new B()` should work fine also (unless the methods are static).
Nate
@Bedwyr The output is just the same! :-)
KLE
+4  A: 

That code will output B if you:

(new B()).run();

Whatever the problem is, it's not in the code you've quoted.

Updated (after your edit)

If the third-party program is calling A.main(), there's nothing (reasonable) you can do in B that will inject itself into A. As long as A.main is doing new A().run(), it's going to have an instance of A, not an instance of B. There's no "current class name" to use, or if there is (depends on your point of view), it's A, not B.

You'll have to get the third-party program to call B in some way, rather than A, or just modify A directly (e.g., getting rid of B entirely). You do not want to modify A to make it use B; that tightly binds it to a descendant and makes the separation between them largely pointless.

Hope that helps.

T.J. Crowder
It's a shame Java doesn't expose the current class the code resides in. I'm more familiar with Python, where I'd be able to do type(self) or explicitly define main() as a classmethod. Anyways, I ended up simply giving B its own main() that instantiates B directly, and that fixes the problem.
Chris S
@Chris: The problem isn't that Java doesn't expose it (it does; within an instance method, `this.getClass()` gives you the `Class` instance that tells you everything you need to know about the class; if you're in a class (static) method, you already know what class you're in and can use the name -- e.g., `A` -- directly to get the `Class` instance). That's not the problem you're having. The problem you're having is that `B` just isn't involved in any way shape or form when `A.main` is called. `B` may as well not be there. It's not a language thing, it's a what-you're-trying-to-do thing. :-)
T.J. Crowder
@T.J. Crowder:Correct, but only because in Java I have no choice but to hardcode "new A()". In Python, main() could be a classmethod, whose first argument would automatically be "cls", so I could instantiate the class by simply doing "cls()". This would be inheritable, so I wouldn't have to write a separate main() for B.
Chris S
@Chris: But again, so you instantiate with `cls()`. You're still going to be instantiating an `A`, not a `B`! This is my point: `B` is just totally not involved in the code that starts things off (in the third-party tool) is calling `A` and `A` has no knowledge of `B`.
T.J. Crowder
@T.J. Crowder: I think I see the miscommunication. The third party tool isn't hardcoded to use A. I pass that in as an argument. What I don't understand is how to get A's main() to create an instance of the class it's inside, whether directly in A or inherited from B.
Chris S
This discussion seems unrelated, so I've posted the question separately at http://stackoverflow.com/questions/2157159/calling-an-inherited-class-method-from-java
Chris S
@Chris: I understand what you're trying to do now. I'll comment over there, but basically, I wouldn't recommend doing that.
T.J. Crowder
+1  A: 

It depends of instantiating. Try this:

 A v1 = new A();
 A v2 = new B();
 B v3 = new A();
 B v4 = new B();

 v1.run()
 v2.run()
 v3.run()
 v4.run()
demas
B v3 = new A(); will not compile, it is impossible !
KLE
This will result in a compilation error on line 3. A is not of type B.
Michael Krauklis
Yes, I know it, but this result is a usefull experience.
demas
@demas Then I suggest writing a comment on the line, to warn unsuspecting readers... Because for one that might (?) try it, hundreds will read it! ;-)
KLE
+2  A: 

Works for me.

Here's my code for A and B:

package so;

public class A{
    public void show(){
        System.out.println("A");
    }
    public void run(){
        show();
    }
}

class B extends A{
    @Override
    public void show(){
        System.out.println("B");
    }
}

Here's my entry point:

package so;

public class EntryPoint {

    public static void main(String[] args) {
        B b = new B();
        b.run();
    }
}

It prints out 'B'.

Nathan Hughes
+1  A: 

I tried your example and my output was B.

How are you instantiating? Here's the exact code I ran.

public class Test {
    public static class A {
        public void show() {
            System.out.println("A");
        }

        public void run() {
            show();
        }
    }

    public static class B extends A {
        @Override
        public void show() {
            System.out.println("B");
        }
    }

    public static void main(String args[]) {
        A a = new B();

        a.run();
    }
}
Michael Krauklis
+1  A: 

If your external program instantiates A, you will have A, not B.

But you can try something like this, using some reflection, and pass "com.mypackage.A" or "com.mypackage.B" as arguments to your program.

With this code (exception catch missing), you will be able to print "A" or "B" depending on the string parameter that you pass.

public static void main( String[] arg ) {
    String className = arg[0];
    Class myClass  = Class.forName(className);
    Constructor cons = myClass.getConstructor(new Class[0]);
    A myObject = (A) cons.newInstance(new Object[0]);
    myObject.show();

}
Laurent K