Java: Как написать метод для принятия ребенка без приведения к родителю? - PullRequest
1 голос
/ 17 декабря 2009

Не уверен, как озаглавить это ...

Итак, у меня есть три дочерних класса Event: WeightEvent, TimedEvent, RepEvent. Любым способом я получаю объект от одного из детей. Теперь я хочу отправить это дочернее событие в метод другого объекта, чтобы он мог извлечь из него данные с помощью метода getSavedEvents(). Этот метод существует только в дочерних элементах, поскольку получение данных зависит от типа события.

Я начал с

public void setEvent(Event e) {

но это приводило мой дочерний объект к Event (родительскому) объекту.

Есть ли способ обойтись без написания трех разных методов. Один друг для детей?

public void setEvent(WeightEvent e) {
public void setEvent(TimedEvent e) {
public void setEvent(RepEvent e) {

Спасибо за любой совет.

-John

Ответы [ 8 ]

7 голосов
/ 17 декабря 2009

Несмотря на то, что ссылка приведена, она не меняет тип реального объекта. Когда вы передаете ссылку, она все равно будет ссылкой на экземпляр дочернего объекта. Обычно этого было бы достаточно, с соответствующими абстрактными методами в родительском типе, если необходимо.

Однако, если вы хотите, чтобы методы были специфичны для типов потомков, и вы не можете придумать подходящую абстракцию, которую все они могут реализовать в общем, то либо вы должны использовать instanceof в своем setEvent code или вам do приходится перегружать ваш метод ... потому что вам придется вызывать разные биты кода в зависимости от точного типа события.

Это все немного расплывчато, потому что мы не можем видеть ваш код, кроме пары сигнатур методов. Если бы вы могли дать нам более подробную информацию о том, что вы пытаетесь сделать, особенно в том, что нужно для достижения setEvent и каковы различные методы в дочерних классах, мы могли бы помочь больше.

3 голосов
/ 19 декабря 2009

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

Используя этот шаблон, ваш класс EventTable становится примерно таким:

public class EventTable {
  public void setEvent(Event e) {
    int x = 0;
    columns = e.getFields();
    Event[] savedEvents = e.getSavedEvents();
    for(Event ev : savedEvents) {
      tempdata[x] = ev.getTempData();
      x++;
    }
  }
}

Обратите внимание, что весь переключатель был заменен одним вызовом getTempData (). Затем этот метод является абстрактным в Event, как getSavedEvents:

public abstract class Event {
  public Date getDate() { return(_date); }
  public abstract Event[] getSavedEvents();
  public abstract int[] getTempData();
  public int[] getFormattedDate() {
    ...

}

Затем вы определяете метод getTempData () в каждом подклассе. Например:

public class WeightEvent extends Event {
  public int getWeight() { return(_weight); }
  public int getReps() { return(_reps); }
  public int[] getTempData() {
    return new int[]{
      getFormattedDate()[0],
      getWeight(),
      getReps()
    };
  }
}

public class TimedEvent extends Event {
  public String getTimeInHMS() { return(_timeString); }
  public int[] getTempData() {
    return new int[]{
      getFormattedDate()[0],
      getTimeInHMS()
    };
  }
}

public class RepEvent extends Event {
  public int getReps() { return(_reps); }
  public int[] getTempData() {
    return new int[]{
      getFormattedDate()[0],
      getReps()
    };
  }
}
1 голос
/ 17 декабря 2009

Я думаю, что ваш пробм вызывает getSavedEvents() при наличии переменной Event.
Если это так, добавьте абстрактный getSavedEvents() метод к Event, который также должен быть объявлен как абстрактный:

    public abstract class Event {
        public abstract Events getSavedEvents();
        ...
    }

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

    public class Event {
        public Events getSavedEvents() {
            throw new UnsupportedOperationException("must be called in a child class");
            // OR return null;
        ...
    }

теперь вы можете вызывать метод getSavedEvents() в другом объекте:

    public class OtherObject {
        private Event event;
        public void setEvent(Event e) {
            event = e;
            ...
            Events events = event.getSavesEvents();

будет использован метод, реализованный вещественным классом e, например, если e является TimedEvent, будет вызван метод в этом классе.

1 голос
/ 17 декабря 2009

Вы можете использовать дженерики, чтобы сделать это.

Определите класс Event следующим образом:

public abstract class Event<T extends Event> {
    public abstract void setEvent(T e);
}

Это определяет класс, который предполагается создать с любым типом, который расширяет Event.

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

class WeightEvent extends Event<WeightEvent>
{

    @Override
    public void setEvent(WeightEvent e) {
        ...
    }

}
0 голосов
/ 17 декабря 2009

Использование abstract помогло с методом getSavedEvents (), так как все дочерние элементы реализуют этот метод.

Вот код для setEvent ():

public class EventTable {
public void setEvent(Event e) {
 int x = 0;
 int type = e.getEventType();

 columns = e.getFields();
 Event[] savedEvents = e.getSavedEvents();
 for(Event ev : savedEvents) {
  tempdata[x][0] = ev.getFormattedDate()[0];
  switch(type) {
   case EVENTTYPE.WEIGHT:
    tempdata[x][1] = ev.getWeight();
    tempdata[x][2] = ev.getReps();
   break;
   case EVENTTYPE.TIMED:
    tempdata[x][1] = ev.getTimeInHMS();
   break;
   case EVENTTYPE.REP:
    tempdata[x][1] = ev.getReps();
   break;
  }
  x++;
 }
}
}

Этот код работает после того, как я добавил «abstract» в класс Event и определил абстрактный метод getSavedEvents ().

Следующая проблема - методы getWeight (), getReps () и getTimeInHMS (). Они специфичны для типа дочернего события и снова не существуют в родительском классе Event. Если я сделаю их абстрактными в Event, теперь я должен определить их в каждом дочернем элементе, хотя getReps () не имеет контекста для TimedEvent.

public class Event {
 public Date getDate() { return(_date); }
}
public class WeightEvent extends Event {
 public int getWeight() { return(_weight); }
 public int getReps() { return(_reps); }
}
public class TimedEvent extends Event {
 public String getTimeInHMS() { return(_timeString); }
}
public class RepEvent extends Event {
 public int getReps() { return(_reps); }
}

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

Если я не делаю getWeight (), getReps () абстрактными и объявляю их только в дочернем объекте, где они актуальны, вот ошибка, которую я получаю из EventTable в вышеописанном скопированном методе setEvent ():

EventTable.java:124: cannot find symbol
symbol  : method getWeight()
location: class Event
     tempdata[x][1] = ev.getWeight();

-John

0 голосов
/ 17 декабря 2009

Может быть, вы можете использовать Шаблон посетителя .

0 голосов
/ 17 декабря 2009

Вы можете абстрагировать проблему за интерфейс

interface IEvent
{
    abstract public void doSomething();
}

Затем все ваши классы событий реализуют его, например,

class WeightedEvent implements IEvent
{
    public void doSomething()
    {
        // do something
    }
}

Тогда вам нужен только один метод и не нужно выполнять проверку типов

public void setEvent(IEvent e)
{
    e.doSomething();
}

НТН

0 голосов
/ 17 декабря 2009

Вы можете привести объект Event e к дочерним классам - я думаю, что оператор instanceof в Java поможет вам.

...