0

I'm working on an inventory program in Java. I have each object in the inventory stored as a relavent class type in a DefaultListModel and JList for each location; for example, if I have a video called "Mulan" and a generic thing called "Ball" in a location "Dorm", in the JList called "Dorm", "Mulan" will be an instance of video and "Ball" will be an instance of thing. All the classes are inherited from thing.

I'm trying to do something like this...

Dorm.getSelectedValue().methodInVideoOrThing;

...but when I try it, it says:

error: cannot find symbol
      Dorm.getSelectedValue().methodInVideoOrThing();
                             ^
symbol:   method methodInVideoOrThing()
location: class Object

Because the DefaultListModel stores each object in a generic object variable, I'm not able to access the methods for any of the classes I made. I tried this...

class c = Dorm.getSelectedValue().getClass();
c A = (c) Dorm.getSelectedValue();
A.methodInC;

... but it returned the following error:

error: cannot find symbol
      c A = (c) Dorm.getSelectedValue();
      ^
symbol: class c

error: cannot find symbol
      c A = (c) Dorm.getSelectedValue();
             ^
2 errors
symbol: class c

I know I could just cycle through all the classes using isInstanceOf and downcast on a case by case basis, but that would be quite tedious. Plus, when I do...

System.out.println(Dorm.getSelectedValue().getClass());

...it returns either class video or class thing, depending on whether "Ball" or "Mulan" is selected, so I know that java knows what class it is.

So, is there some way I can access the methods of a subclass, when that subclass is stored as a variable of type object, without downcasting? Or is there a way to do this with downcasting and I'm just doing it wrong?

LMNOP
  • 151
  • 6

2 Answers2

0

as such there is no way to use sub-type methods from super-type other than down-casting. so better u could set up a if-else ladder

if (o instanceof Type1)
{
((Type1)0)MethodOfType1
}
else if
{
...
}

But in my opinion, i would rather prefer to add 1 more level of class above your things & video, and provide a generic method for things and video.

Ankit
  • 6,554
  • 6
  • 49
  • 71
  • Sadly a generic method doesn't really work for what I want (see the reply I posted to his comment for details), but the Handler idea Adriaan suggested will work. At least I think. – LMNOP Mar 11 '13 at 09:28
0

The issue here is with your Object Oriented design. What kind of method is "methodInVideoOrThing()"? If this is a method that all things (including video's) have in common then it belongs in Thing. If it is a video-specific method then it belongs in Video.

In the first case (method of Thing) you can just cast the value from JList to Thing and you're done:

Thing selectedThing = (Thing) dorm.getSelectedValue();
selectedThing.methodInThing();

In the second case (method of Video) you have the problem you described of having to distinguish between different categories of Things like Videos, Fruit and Clothing. What I think I would do in this case is create separate classes to handle each category and pass the Object to it based on its category, a simple example:

public interface Handler {
    public Class getCategory();
    public void handle(Thing item);
}

public class VideoHandler implements Handler {
    public Class getCategory() {
        return Video.class;
    }

    public void handle(Thing item) {
        if (!item instanceof getCategory()) {
            throw new IllegalArgumentException("Wrong category - can only handle Video");
        }
        Video video = (Video) item;
        // do video-things            
    }        
}

In your calling class you need to select the correct handler based on the category:

private void init() {
    List<Handler> handlers = new ArrayList<Handler>();
    handlers.add(new VideoHandler());
    // add other handlers
}

public void someMethod(Jlist dorm) { 
    Object item = dorm.getSelectedValue();
    for(Handler handler : handlers) {
        if (item instanceof handler.getCategory()) {
            // optionally catch IllegalArgumentException here 
            handler.handle(item);
        }
    }
}

This can be made a bit nicer, for example use an enum as Category instead of the class, use generics to type the Handler and extract any common behavior (like the typechecking) into an abstract class, but this is the gist of it.

Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60
  • Very helpful, thanks. As an answer to the question you had in the first paragraph, the methods aren't consistent. My goal is to have a window pop up with the appropriate methods for the object that is selected; in order to do that, I made a "getInputGUI" method that is defined for all classes, but implemented slightly differently for each class (some are identical, some aren't). – LMNOP Mar 11 '13 at 09:23
  • Also, I managed to get the functionality I wanted using reflection and method.invoke, but what you suggested seems a lot better, so I'll probably change it. – LMNOP Mar 11 '13 at 09:26
  • getInputGUI sounds awfully presentation-specific. Maybe you can modify my handlers so they offer a list of 'Function' objects that match each method in your target class (e.g. Video). Then in your GUI code you just need to create an actual GUI control (like a textbox) for each Function so that the control's input triggers a call on Function to store the value and execute the logic. They key is to consistently use your class hierarchy to split up your complexity into classes and avoid large switch statements or conditionals. – Adriaan Koster Mar 11 '13 at 16:00