Путаница с абстрактным классом Java: переопределенный метод не вызывается - PullRequest
3 голосов
/ 15 марта 2009

Итак, у меня есть два класса. Одно абстрактно:

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 () в подклассе, поэтому я предположил, что он вызовет метод подкласса, но, похоже, нет. Есть ли способ сделать это, или я совершил очевидную ошибку, или я неправильно понял, как должны работать абстрактные классы?

P.S. Я попытался поместить оператор print внутри подкласса displayOptions (), чтобы убедиться, что я ничего не сделал с помощью метода, который я вызываю.

Большое спасибо,

Адам

Ответы [ 4 ]

4 голосов
/ 15 марта 2009

Возможно, что-то не так с вашим 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 голосов
/ 15 марта 2009

Это тот случай, когда классы находятся в разных пакетах? Если это так, вам нужно объявить метод для переопределения как защищенный.

Редактировать: (думаю, объяснение может помочь: -)

Если вы объявите метод public / protected, то он может быть переопределен дочерними элементами вне пакета. Если вы делаете это (пакет) / частный, то это невозможно. закрытые методы вообще не могут быть переопределены, какие (пакетные) могут быть переопределены только классами в одном пакете.

Я использую (package), потому что для него нет ключевого слова, поэтому при отсутствии публичного / защищенного / приватного доступа вы получаете (package) доступ.

Edit:

Сказанное выше, вероятно, неверно, учитывая ваше описание (при условии, что класс действительно является абстрактным, и вы использовали аннотацию @Override).

Вы на 100% уверены, что вызывается метод run? Запустите System.out.println и убедитесь, что он вызывается.

Вы на 100% уверены, что не перехватываете какие-либо другие исключения и не распечатываете трассировку стека (или что-то еще, что будет гарантировать, что вы видите, что исключение было перехвачено)?

0 голосов
/ 30 сентября 2009

Эта тема некоторое время была тихой, поэтому я сомневаюсь, что это поможет вам, но я подумал, что опубликую ее, если кто-то еще придет искать ответы.

Еще несколько минут назад у меня была похожая проблема с интерфейсом, абстрактным классом и конкретным подклассом. В основном интерфейс определяет 10 методов, абстрактный класс реализует 2 из них, а остальные 8 оставляют для конкретного класса. Реализация одного из методов в абстрактном классе вызывает метод, который должен был быть реализован конкретным классом.

Все скомпилировалось нормально, и NetBeans ни на что не жаловался, но во время выполнения виртуальная машина взорвалась, заявив, что метод (реализованный в конкретном классе) не существует. Прошло много лет с тех пор, как я в последний раз использовал Java, но я почти уверен, что в этой ситуации такое поведение не ожидается (кто-то, пожалуйста, поправьте меня, если я ошибаюсь).

После нескольких часов работы с ноутбуком я обнаружил, что добавление абстрактной заглушки метода, который я вызывал, к абстрактному классу позволяет переадресовывать вызов конкретному классу без каких-либо жалоб со стороны виртуальной машины. Это нормально или мне просто повезло?

В любом случае, надеюсь, кто-нибудь найдет это полезным.

0 голосов
/ 15 марта 2009

Не уверен, в чем проблема, не могли бы вы напечатать какой-нибудь вывод (с вашими инструкциями печати).

Я скопировал / вставил ваш код и, кроме комментариев, закомментировал одну или две строки, где у меня не было подходящего источника для объекта. Это вызвало методы подклассов для меня.

Логически, при чтении вашего кода ничто не казалось неуместным, но я предпочитаю видеть вещи своими глазами, чтобы убедиться, что не было других проблем, и поэтому я сначала попытался запустить ваш код. :)

Вот что я изменил и мой вывод.

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().

Таким образом, похоже, ваш класс работал, возможно, запись не была на должном уровне.

Надеюсь, это поможет.

...