覆写Java方法

5

我是Java的新手,已经阅读了一些有关重写方法的教程,但我正在查看的示例并没有按照我预期的方式工作。例如,我有以下代码:

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");
    }
}

当我实例化并调用B.run()时,我期望看到输出"B"。但实际上我看到的是"A"。我做错了什么?
编辑:是的,这两个类位于两个不同的文件中。它们为了简洁起见在此一起显示。
编辑:我不确定B如何被实例化,因为它是由使用类加载器的第三方程序完成的。
编辑:有关第三方程序的更多信息。它首先调用A.main(),我最初没有显示它(抱歉)。我假设我需要让 "new A().run();" 更加通用,以使用当前类的名称。这是否可能?

5
你是如何实例化并调用它的? - Travis Gockel
1
@Prasoon 没有提到文件,只有类。我们无法推断它们实际所在的文件。 - KLE
6个回答

6
那段代码将在以下情况下输出B
(new B()).run();

无论问题是什么,它都不在你引用的代码中。
更新后:
如果第三方程序调用A.main(),那么在B中没有任何(合理的)方法可以将自己注入到A中。只要A.main()执行new A().run(),它就会有一个A的实例,而不是B的实例。没有“当前类名”可用,或者如果有(取决于您的观点),它是A而不是B。
您需要以某种方式让第三方程序调用B,而不是A,或直接修改A(例如完全摆脱B)。您不想修改A以使用B;这会将其紧密绑定到后代,并使它们之间的分离基本无意义。
希望有所帮助。

很遗憾Java没有暴露代码所在的当前类。我更熟悉Python,在那里我可以做type(self)或显式地将main()定义为classmethod。不管怎样,我最终只是给B一个自己的main(),直接实例化B,问题就解决了。 - Cerin
@Chris: 问题不在于Java没有暴露它(实际上有;在实例方法中, this.getClass() 可以给你一个 Class 实例,告诉你关于类的一切需要了解的信息;如果你在一个类(静态)方法中,你已经知道你在哪个类中并且可以直接使用名称 - 例如, A - 获取 Class 实例)。这不是你遇到的问题。你遇到的问题是当 A.main 被调用时, B 没有以任何方式参与其中。 B 可以完全不存在。这不是一种语言问题,而是你正在尝试做的事情的问题。 :-) - T.J. Crowder
@T.J. Crowder: 没错,但这只是因为在Java中我别无选择,只能硬编码“new A()”。在Python中,main()可以是一个classmethod,它的第一个参数将自动成为“cls”,所以我可以通过简单地执行“cls()”来实例化类。这将是可继承的,因此我不必为B编写单独的main()。 - Cerin
@T.J. Crowder:我想我已经看到了误解所在。第三方工具并非硬编码使用A。我将其作为参数传递。我不明白的是如何让A的main()方法创建一个类的实例,无论是直接在A中还是从B继承。 - Cerin
这个讨论似乎没什么关联,所以我已经在http://stackoverflow.com/questions/2157159/calling-an-inherited-class-method-from-java上单独发布了问题。 - Cerin
显示剩余2条评论

3

我尝试将您的两个类放在两个文件中,结果很好,输出了“B”。我调用:

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

更新:也可以作为(因为它是相同的运行时实例):

 A a = new B();
 a.run();

1
A a = new B() 应该也可以正常工作(除非方法是静态的)。 - Nate
@Bedwyr 输出结果完全一样! :-) - KLE

2

对我来说没问题。

这是我的A和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");
    }
}

这是我的入口点:

package so;

public class EntryPoint {

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

它会打印出字母'B'。

1
它取决于实例化。尝试这个:
 A v1 = new A();
 A v2 = new B();
 B v3 = new A();
 B v4 = new B();

 v1.run()
 v2.run()
 v3.run()
 v4.run()

3
B v3 = new A(); 不会编译,这是不可能的! - KLE
这将导致第3行编译错误。A不是类型B。 - Michael Krauklis
是的,我知道,但这个结果是一个有用的经验。 - ceth
1
那我建议在代码行上写注释,以警告不知情的读者... 因为尽管只有一个人可能会尝试它,但会有成百上千的人阅读它!;-) - KLE

1

我尝试了你的示例,我的输出是B

你是如何实例化的?这是我运行的精确代码。

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();
    }
}

1
如果您的外部程序实例化了A,那么您将获得A而不是B。
但是,您可以尝试使用一些反射,将“com.mypackage.A”或“com.mypackage.B”作为参数传递给您的程序。
使用此代码(缺少异常捕获),您将能够打印“A”或“B”,具体取决于您传递的字符串参数。
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();

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接