Java ArrayList состоит из абстрактного класса и всего, что расширяет его? - PullRequest
3 голосов
/ 09 февраля 2012

Я знаю, что это основной вопрос, но я не могу найти другие посты StackOverflow или какие-либо хорошие документы по API по этому вопросу.

Скажем, у меня есть абстрактный класс, такой как Appliance, а затем у меня есть несколько классовкак Toaster и Blender, которые расширяются Appliance.Теперь предположим, что я хочу создать ArrayList, который будет содержать смешанные элементы, которые в конечном итоге являются членами Appliance, но также могут быть Toaster или Blender.Класс Blender имеет метод с именем turnBlenderOff(), а класс Toaster имеет метод с именем turnToasterOff(), и я захочу перебрать мой ArrayList и вызвать эти методы в зависимости от того, к какому подклассу фактически принадлежит элемент.

В настоящее время я создаю класс с именем PowerPoint и пытаюсь:

 // Constructor given an ArrayList of appliances.
 public PowerPoint(ArrayList<Appliance> initial_list_of_appliances){
     int listSize = initial_list_of_appliances.size();
     for(int ii = 0; ii < listSize; ii++){
         this.applianceList.add(initial_list_of_appliances.get(ii));
     }
 }

 /////
 // Method to switch everything in the list OFF simultaneously.
 /////
 public void switchOff(){
     int N = this.applianceList.size();
     String cur_name;
     for(int ii = 0; ii < N; ii++){
         cur_name = this.applianceList.get(ii).getClassName();
             if(cur_name.equals("Blender")){
                 this.turnBlenderOff(this.applianceList.get(ii));
             }
             else if(cur_name.equals("Toaster")){
                 this.turnToasterOff(this.applianceList.get(ii)); 
             }
             else if(cur_name.equals("Oven")){
                 this.turnOvenOff(this.applianceList.get(ii));      
             }
         }
     }

Большая часть моего кода компилируется нормально, но я продолжаю получать это сообщение об ошибке:

 PowerPoint.java:83: turnBlenderOff(appliances.ApplianceWrapper.Blender) in PowerPoint cannot be applied to (appliances.ApplianceWrapper.Appliance)
      this.turnBlenderOff(this.applianceList.get(ii));

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

Я пытался заменить тип <Appliance> на <? extends Appliance> в спецификациях ArrayList, но это дало дополнительные ошибки и больше не компилировалось.

Как правильно составить список на основе абстрактного типа, но затем вызывать методы подклассного типа, используя что-то вроде getClassName() для извлечения типа подкласса?

Added

Поскольку многие люди сразу указалиочевидное: лучше использовать наследование, мне нужно объяснить.Для этого проекта мы должны предположить, что все подклассы Appliance были созданы сторонними разработчиками и помещены в некоторый пакет, который мы не можем изменить.Это было сделано нехорошо, так как все разные подклассы имеют разные методы включения / выключения, и это нельзя изменить.Таким образом, возможность создания гладкого Appliance абстрактного класса мне не открыта.Например, Toaster имеет метод startToasting(), а Oven имеет метод heatUp(), каждый из которых служит методом «on» для двух разных классов.

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

Ответы [ 6 ]

4 голосов
/ 09 февраля 2012

Если вы хотите использовать абстрактный класс и все подклассы на самом деле имеют одинаковые функции, почему бы вам не использовать функцию в абстрактном базовом классе под названием "turnDeviceOff" и соответственно переопределить ее в подклассах. Это ОО подход.

2 голосов
/ 09 февраля 2012

ArrayList в порядке.

но вы можете сделать это:

public abstract class Appliance{
 //declare an abstract method
abstract void switchOff();
}

тогда

public class Toaster extends Appliance{

//implement the abstract method
void switchOff(){
 //do toaster switchOff
}

}

для других подклассов, сделайте то же самое.

наконец,

for(Appliance element: yourList){
element.switchOff();
}
2 голосов
/ 09 февраля 2012

Используйте instanceof или getClass, не катя свой собственный getClassName, а затем выполните явное приведение к типу, который вы только что определили.

Тем не менее, предпочтите ответ @ guitarflow, хотя этот подход может не сработать, если есть состояние, которое нельзя просто передать методу switchOff.

1 голос
/ 09 февраля 2012

Есть два способа сделать это. Первый (и менее рекомендуемый ) способ заключается в использовании ключевого слова instanceof и приведении вашего экземпляра Appliance в экземпляр Blender :

for(Appliance a : list){
    if(a instanceof Blender) this.turnBlenderOff((Blender)a);
    ...
}

Это плохо, потому что instanceof медленен и не позволяет вам воспользоваться преимуществом самого мощного аналога Java для полиморфизма, позднее связывание . Лучше всего было бы, чтобы у класса Appliance был абстрактный публичный метод с именем turnOff () . Тогда вы могли бы сделать что-то вроде:

for(Appliance a : list){
    a.turnOff();
    ...
}
0 голосов
/ 09 февраля 2012

Чтобы решить эту проблему, добавьте метод turnOff() в класс Appliance и соответствующим образом переопределите их различными подклассами.Если вы сделаете это, большой код «если» исчезнет.

0 голосов
/ 09 февраля 2012

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

Иначе, какой смысл в том, чтобы они расширяли Appliance в первую очередь?

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

Редактировать

Подсказка по случайному стилю: почти никогда не нужно использовать индексный доступ на List s:

public PowerPoint(List<Appliance> initialList){
   for(Appliance app : initialList)
     applianceList.add(app);
}

Конечно, есть также:

applianceList.addAll(initialList);

Редактировать 2

Более прямой перевод:

public void switchOff(){
    for(Appliance app : applianceList)
        switchOff(app);
}

private void switchOff(Appliance app){
    if(app instanceof Blender)
        turnBlenderOff(app);
    else if(app instanceof Toaster)
        turnToasterOff(app);
    else if(app instanceof Oven)
        turnOvenOff(app);
    else
        throw new RuntimeException("unknown appliance: " + app);
}

Вы также можете добавить оболочку для различных классов устройств, которая нормализует API, но это может не стоить (в зависимости от степени вовлеченности).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...