Java抽象类混淆:重写的方法未被调用

3

So I have two classes. One is abstract:

public abstract class AbstractClient {
    protected boolean running = true;

    protected void run() {
        Scanner scanner = new Scanner(System.in);
        displayOptions();
        while (running) {
            String input = null;
            while (scanner.hasNext()) {
                input = scanner.next();
            }
            processInputCommand(input);
        }
    }

    abstract void displayOptions();

    abstract void processInputCommand(String input);

}

其中一个是具体的子类:

public class BasicClient extends AbstractClient {
    private IBasicServer basicServer;

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

    public BasicClient() {
        try {
            System.setSecurityManager(new RMISecurityManager());
            Registry registry = LocateRegistry.getRegistry();
            basicServer =  (IBasicServer) registry.lookup(IBasicServer.LOOKUPNAME);
            run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    void displayOptions() {
        BasicClientOptions.displayOptions();

    }

    @Override
    void processInputCommand(String input) {
        // TODO Auto-generated method stub

    }
}

现在在子类中,我调用了抽象类的run()方法,因为这应该是所有客户端共有的。在run()方法内部调用了抽象方法displayOptions()。
我在子类中重写了displayOptions()方法,所以我认为它会调用子类的方法,但似乎并没有。是否有方法可以实现这一点,或者我犯了一个明显的错误,或者我对抽象类的工作原理存在误解?
顺便说一句,我尝试在子类的displayOptions()方法中放置了一个打印语句,以确保我没有使用我调用的方法做了傻事。
非常感谢,
亚当
4个回答

5
也许您的 BasicClientOptions.displayOptions() 方法调用存在问题。我想知道您如何确定未调用 BasicClient.displayOptions() 方法。
这是您所拥有的简化版本,请运行它。 它的行为方式符合您的预期。
public abstract class BaseClass {
    public void run() { foo(); }
    public abstract void foo();
}

public class Subclass extends BaseClass {

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

    @Override
    public void foo() {
        System.out.println("I'm from the subclass");
    }
}

2
类是否在不同的包中?如果是,那么您需要将要覆盖的方法声明为protected。
编辑:(猜测解释可能有所帮助:-)
如果您声明一个公共/受保护的方法,那么它可以被包外的子类重写。如果您将其设置为(包)/私有,则无法重写。私有方法根本无法重写,而(包)方法只能由同一包中的类重写。
我使用(包)是因为没有关键字可以使用,因此在缺少public/protected/private的情况下,您将获得(包)访问权限。
编辑:
鉴于您的描述(假设该类确实是抽象的,并且您已经使用了@Override注释),上述内容可能不正确。
您是否100%确定正在调用run方法?在run中放置System.out.println并确保它被调用。
您是否100%确定没有捕获任何其他异常并未打印出堆栈跟踪(或其他将确保您看到已捕获异常的内容)?

豆腐啤酒——你是对的。人们需要注意Java中的默认保护! - Pat

0

不确定问题出在哪里,您能否打印一些输出(使用print语句)。

我复制/粘贴了您的代码,除了注释掉一两行因为我没有正确的对象来源之外。它为我调用了子类的方法。

逻辑上,阅读您的代码,似乎没有什么不对,但我更喜欢亲眼看到东西,以确保没有其他问题,所以我先尝试运行了您的代码。 :)

这是我修改的部分和我的输出结果。

import java.util.Scanner;

public abstract class AbstractClient {
  protected boolean running = true;

  protected void run() {
    Scanner scanner = new Scanner( "foo\\r\\nbar\\r\\n" );
    displayOptions();
    while ( running ) {
      String input = null;
      while ( scanner.hasNext() ) {
        input = scanner.next();
      }
      processInputCommand( input );
      running = false;
    }
  }

  abstract void displayOptions();

  abstract void processInputCommand( String input );

}

import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class BasicClient extends AbstractClient {
  //private IBasicServer basicServer;

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

  public BasicClient() {
    try {
      System.setSecurityManager( new RMISecurityManager() );
      Registry registry = LocateRegistry.getRegistry();
      //basicServer =  (IBasicServer) registry.lookup(IBasicServer.LOOKUPNAME);
      run();
    } catch ( Exception e ) {
      e.printStackTrace();
    }
  }

  @Override
  void displayOptions() {
    //BasicClientOptions.displayOptions();
    System.out.println( "We're in subclasses displayOptions()." );
  }

  @Override
  void processInputCommand( String input ) {
    System.out.println( "We're in subclasses processInputCommand()." );
  }
}

我的输出

We're in subclasses displayOptions().
We're in subclasses processInputCommand().

所以实际上看起来你的类是在工作,只是可能日志记录不够完善。

希望这可以帮到你。


在我的帖子下面添加评论,我应该补充说,将扫描仪保留为原样并且没有任何输入将会阻塞,因此processInputCommand()不会立即被调用。这就是为什么我通过硬编码一个fo\r\nbar\r\n来破坏了你的扫描仪。更多是为了演示目的。抱歉。 - JeanNiBee

0

这个帖子已经安静了一段时间,所以我怀疑这不会对你有帮助,但我想发布它以防其他人寻找答案。

直到几分钟前,我也遇到了一个类似的问题,涉及接口、抽象类和具体子类。基本上,接口定义了10个方法,抽象类实现其中2个并将其余8个留给具体类。抽象类中一个方法的实现调用了一个应该由具体类实现的方法。

所有东西都编译得很好,NetBeans没有抱怨任何事情,但在运行时,VM崩溃了,指出该方法(在具体类中实现的方法)不存在。距离我上次使用Java已经过去多年了,但我相信这不是这种情况下预期的行为(如果我错了,请有人纠正我)。

经过几个小时的折腾,我发现在抽象类中添加一个我正在调用的方法的抽象存根,可以将调用转发到具体类,而不会引起VM的任何投诉。这是正常的吗?还是我只是走了运?

无论如何,希望有人会发现这个有用。


不,这不正常。听起来像是某些东西没有正确编译。比如在运行时你使用了旧版本的接口。通过将方法添加到抽象类中,即使在运行时你使用了旧版本的接口,也可以解决问题。 - Yishai

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