views:

763

answers:

4

Hi,

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

}

One is the concrete subclass:

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

    }
}

Now in the subclass I call the run() method of the abstract class because this should be common to all clients. Inside the run() method is a call to the abstract method displayOptions().

I have overridden displayOptions() in the subclass so I assumed that it would invoke the subclassed method but it seems it has not. Is there a way to do this or have I made an obvious mistake or have I misunderstood how abstract classes should work?

P.S I tried putting a print statement inside the subclassed displayOptions() to ensure I hadn't done something daft with method I call.

Many thanks,

Adam

+4  A: 

Maybe something is wrong with your BasicClientOptions.displayOptions() call. I'm wondering how you know that BasicClient.displayOptions() isn't being called.

Here's a simplified version of what you have. Try running it. It behaves in the way you expect.

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");
    }
}
Willie Wheeler
A: 

Not sure what the problem is, could you print some output (with your print statements).

I copy / pasted your code and aside from commenting out a line or two where I did'nt have the proper source for the object. It called the subclasses methods for me.

Logically, reading your code nothing seemed out of place but I prefer to see stuff with my own eyes to make sure there wasn't another issues and so I tried running your code first. :)

Here's what I modified and my output.

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

My Output

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

So in effect it seems your class was working just maybe the logging wasn't up to par.

Hope this helps.

Comment to my own post, I should add that leaving teh scanner as is and not having any input will block so the processInputCommand() wont be called righty away. That's why I borked up your scanner by hard coding a fo\r\nbar\r\n into it. More for demo purposes.Sorry.
+2  A: 

Is it the case that the classes are in different packages? If so you need to declare the method to override as protected.

Edit: (guess an explanation might help :-)

If you declare a method public/protected then it can be overridden by children outside of the package. If you make it (package)/private then it cannot. private methods cannot be overridden at all which (package) ones can only be overridden by classes in the same package.

I use (package) because there is no keyword for it, so in the absence of public/protected/private you get (package) access.

Edit:

The above is likely not true given your description (assuming that the class really is abstract, and you have used the @Override annotation).

Are you 100% sure that the run method is getting called? Put a System.out.println in run and make sure it is called.

Are you 100% sure that you are not catching any other exceptions and failing to print out the stack trace (or something else that will ensure that you see that the exception was caught)?

TofuBeer
TofuBeer -- you are right. People need to pay attention to the default protection in java!
Pat
A: 

Hi

This thread has been quiet for a while, so I doubt this will help you, but I thought I'd post it in case somebody else comes looking for answers.

Up until a few minutes ago, I was having a similar problem with an interface, abstract class, and concrete subclass. Basically the interface defines 10 methods, the abstract class implements 2 of those and leaves the other 8 for the concrete class. The implementation for one of the methods in the abstract class calls a method that was meant to be implemented by the concrete class.

Everything compiled fine and NetBeans didn't complain about anything but at runtime the VM bombed out, stating that the method (the one implemented in the concrete class) did not exist. It's been years since the last time I used Java, but I'm pretty sure this isn't the expected behavior in this situation (somebody please correct me if I'm wrong).

After several hours of kicking my laptop, I discovered that adding an abstract stub of the method I was calling to the abstract class allowed the call to be forwarded to the concrete class without any complaints from the VM. Is this normal or did I just get lucky?

Anyway, hope somebody finds this useful.

Rory
No its not normal. It sounds like something didn't compile correctly. Say you had old version of the interface during runtime. By adding the method to the abstract class, you made the problem go away even if you have an old version of the interface at runtime.
Yishai