I know this is a basic question, but I can’t find other StackOverflow posts or any good API docs on this.
Say I have an abstract class like Appliance and then I have some classes like Toaster and Blender that extend Appliance. Now suppose that I want to create an ArrayList that will contain mixed elements, all of which are ultimately members of Appliance but could also be Toaster or Blender as well. The Blender class has a method called turnBlenderOff() and the Toaster class has a method called turnToasterOff(), and I will want to iterate over my ArrayList and call these methods, depending on which subclass the element actually belongs to.
Currently I make a class called PowerPoint and try:
// 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));
}
}
}
Most of my code compiles fine, but I keep getting this error message:
PowerPoint.java:83: turnBlenderOff(appliances.ApplianceWrapper.Blender) in PowerPoint cannot be applied to (appliances.ApplianceWrapper.Appliance)
this.turnBlenderOff(this.applianceList.get(ii));
I see that this method, implemented to work only on Blender objects is trying to be executed on an Appliance object that happens to be a Blender but that the compiler doesn’t realize this.
I tried to replace the <Appliance> type with <? extends Appliance> in the ArrayList specifications, but that gave additional errors and would not longer compile.
What is the proper way to make a list based on the abstract type, but then call methods of the subclassed type by using something like getClassName() to retrieve the subclass type?
Added
Since a lot of folks immediately pointed out the obvious: use inheritance better, I need to explain. For this project, we have to assume that all of the subclasses of Appliance were created by third-party people and put into some package that we cannot change. This was done in a bad, crufty way in which all different subclasses have different on/off methods and this can’t be changed. So the option of designing a smooth Appliance abstract class is not open to me. For example, Toaster has the method startToasting(), while Oven has the method heatUp(), each of which serves as the ‘on’ method for the two different classes.
I think the exercise is meant to teach us: how would you retro-fit someone else’s bad design job so as to minimize future damage.
Use
instanceoforgetClass, not rolling your owngetClassName, and then do an explicit cast to the type you just identified.That said, prefer @guitarflow’s answer, though that approach might not work if there is state that you can’t just pass to the
switchOffmethod.