Доступ к методам из подкласса, хранящегося как суперкласс Java (понижение?)

Я работаю над программой инвентаризации на Java. У меня есть каждый объект в инвентаре, хранящийся как относительный тип класса в DefaultListModel и JList для каждого местоположения; например, если у меня есть видео под названием «Мулан» и общая вещь под названием «Мяч» в локации «Общежитие», в JList под названием «Общежитие», «Мулан» будет экземпляром video, а «Мяч» будет экземпляр thing. Все классы наследуются от thing.

Я пытаюсь сделать что-то вроде этого...

Dorm.getSelectedValue().methodInVideoOrThing;

... но когда я пытаюсь это сделать, он говорит:

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

Поскольку DefaultListModel хранит каждый объект в универсальной переменной object, я не могу получить доступ к методам любого из созданных мною классов. Я пробовал это...

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

... но он вернул следующую ошибку:

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

Я знаю, что мог бы просто перебрать все классы, используя isInstanceOf, и приводить их к понижению в каждом конкретном случае, но это было бы довольно утомительно. К тому же, когда я...

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

... он возвращает либо class video, либо class thing, в зависимости от того, выбран ли «Мяч» или «Мулан», поэтому я знаю, что java знает, какой это класс.

Итак, есть ли способ получить доступ к методам подкласса, когда этот подкласс хранится как переменная типа объекта без понижения? Или есть способ сделать это с понижением, и я просто делаю это неправильно?


person LMNOP    schedule 11.03.2013    source источник


Ответы (2)


Проблема здесь в вашем объектно-ориентированном дизайне. Что за метод "methodInVideoOrThing()"? Если это метод, общий для всех вещей (включая видео), то он принадлежит Thing. Если это метод, специфичный для видео, то он относится к видео.

В первом случае (метод Thing) вы можете просто привести значение из JList в Thing, и все готово:

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

Во втором случае (метод видео) у вас возникает описанная вами проблема, связанная с необходимостью различать разные категории вещей, таких как видео, фрукты и одежда. Я думаю, что в этом случае я бы создал отдельные классы для обработки каждой категории и передал бы ему объект на основе его категории, простой пример:

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            
    }        
}

В вашем вызывающем классе вам нужно выбрать правильный обработчик на основе категории:

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

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

person Adriaan Koster    schedule 11.03.2013
comment
Очень полезно, спасибо. Как ответ на вопрос, который вы задали в первом абзаце, методы несовместимы. Моя цель - открыть всплывающее окно с соответствующими методами для выбранного объекта; для этого я создал метод getInputGUI, определенный для всех классов, но реализованный для каждого класса немного по-разному (некоторые из них идентичны, некоторые — нет). - person LMNOP; 11.03.2013
comment
Кроме того, мне удалось получить желаемую функциональность с помощью рефлексии и method.invoke, но то, что вы предложили, кажется намного лучше, поэтому я, вероятно, изменю его. - person LMNOP; 11.03.2013
comment
getInputGUI звучит ужасно специфично для презентации. Может быть, вы можете изменить мои обработчики, чтобы они предлагали список объектов «Функция», которые соответствуют каждому методу в вашем целевом классе (например, видео). Затем в вашем коде графического интерфейса вам просто нужно создать фактический элемент управления графическим интерфейсом (например, текстовое поле) для каждой функции, чтобы ввод элемента управления вызывал вызов функции для сохранения значения и выполнения логики. Ключевым моментом является последовательное использование иерархии классов для разделения сложности на классы и избегание больших операторов переключения или условий. - person Adriaan Koster; 11.03.2013

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

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

Но, на мой взгляд, я бы предпочел добавить еще 1 уровень класса над вашими вещами и видео и предоставить общий метод для вещей и видео.

person Ankit    schedule 11.03.2013
comment
К сожалению, общий метод на самом деле не работает для того, что я хочу (подробности см. В ответе, который я разместил на его комментарий), но идея обработчика, предложенная Адрианом, будет работать. По крайней мере, я думаю. - person LMNOP; 11.03.2013