用超类扩展 javafx.application.Application。为什么 Override 在子类中被忽略?

Extending javafx.application.Application with superclass. Why is Override ignored in subclass?

我需要定义一个 类型 的应用程序,我从中扩展实际应用程序(例如在下面的 TestApp_sub 中)。遗憾的是,这些实际的应用程序覆盖方法不起作用。似乎他们被完全忽略了。下面的包应该打印

A
B

执行中。但只打印

A

相反。


代码

超级class 基于javafx:

package overrideIssue;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class TestApp_super extends Application {

        @Override
        public void start(Stage stage) {
            Scene scene = new Scene(new Pane(), 640, 480);
            stage.setScene(scene);
            stage.show();

            myMethod();
        }

        public static void main(String[] args) {
            launch();
        }

        public void myMethod(){
            System.out.println("A");
        }
}

子class(一个实际的应用程序/包含被忽略的覆盖):

package overrideIssue;

public class TestApp_sub extends TestApp_super {

    @Override
    public void myMethod() {
        super.myMethod();
        System.out.println("B");
    }
}

主要方法:

package overrideIssue;

public class App {
    public static void main( String[] args ) {
        TestApp_sub.main( args );
    }
}

建议

根据 javatpoint 应满足以下条件: Java 方法覆盖的规则

(但是它是一个 javafx.application.Application,它没有被实例化为一个对象。) 我在 Whosebug 上发现了几个看似相似的问题,但是我看不到这个问题的解决方案。非常感谢。

这里的问题是 Application.launch 的工作方式。遍历调用堆栈以确定调用 Application.launch 的 class 并构建此 class 的实例以用于启动应用程序。您没有改变 main 方法调用 Application.launchTestApp_super class.

的一部分这一事实

不是编译器在编译时确定 class 包含 TestApp_sub.main( args );main 方法,因此字节代码中没有留下关于您正在使用 TestApp_sub.

您可以将应用程序 class 传递给 launch 的重载版本:

Application.launch(TestApp_sub.class, args);

来自 Application#launch(String...) 的文档:

Launch a standalone application. This method is typically called from the main method. It must not be called more than once or an exception will be thrown. This is equivalent to launch(TheClass.class, args) where TheClass is the immediately enclosing class of the method that called launch [emphasis added].

在你的例子中,调用 launch 的方法的 "immediately enclosing class" 是 TestApp_super。这意味着实例化并用作应用程序 class 的 TestApp_super,而不是 TestApp_sub。换句话说,您没有将 class 与覆盖的方法一起使用,您看到的输出是预期的。

要使用您想要的class,至少有三个选项:

  1. 使用Application#launch(Class,String...):

    package overrideIssue;
    
    import javafx.application.Application;
    
    public class App {
        public static void main( String[] args ) {
            Application.launch(TestApp_sub.class, args);
        }
    }
    

    现在您明确告诉 JavaFX 哪个 class 用作应用程序 class。您甚至可以添加逻辑来检查命令行参数并决定将哪个 class 传递给 launch.

  2. 只需将您想要的应用程序作为主要应用程序即可class。换句话说,在命令行上指定适当的class。

  3. TestApp_sub class 中添加一个 public static void main(String[]) 方法。

    package overrideIssue;
    
    public class TestApp_sub extends TestApp_super {
    
        public static void main(String[] args) {
            launch(args);
        }    
    
        @Override
        public void myMethod() {
            super.myMethod();
            System.out.println("B");
        }
    }
    

    这不会覆盖TestApp_superclass中的main方法,它只会隐藏 it——静态方法不能被覆盖。调用哪个静态方法由调用该方法的声明类型决定;注意我说的是声明类型,而不是运行时类型,因为要调用的静态方法是在编译时决定的。由于您使用 TestApp_sub.main(args) 中的方法 TestApp_sub 被调用(假设您已经像上面那样添加了它)。但是,如果您要使用 TestApp_super.main(args),它将调用 TestApp_super 中的方法,而不管子 class.

    中是否存在具有相同签名的方法

    现在 TestApp_sub 是调用 launch 的方法的 "immediately enclosing class",它会被实例化和使用 TestApp_sub