Search code examples
javapolymorphismabstract-class

Getting The Best Out of Polymorphism in Java


I have the following problem:

Consider we have 3 classes :

  • One abstract class named Vehicle
  • One class named Motorcycle which extends Vehicle
  • One class named Car which extends Vehicle

That means that Vehicle objects can hold both Motorcycle and Car objects.

I have one more class named VehicleDatabase which basically "simulates" a database ( I wish I could use databases but this is an university project) and holds an ArrayList<Vehicle> field named dbtogether with some methods that provide some basic operations so I can interact with the database (CRUD operations and some others).

One method I want to implement is the showVehicles(). To simplify and get to the point of my question let's say that this method must simply print to the console the .toString()'s of all object specified by the function's parameter.

Example:

A menu shows up in the user:

Select which type of vehicles you want to be shown:

1.Motorcycles
2.Cars

Make your choice: 

After that our function showVehicles must take a parameter e.g showVehicles(CarSomething) and print all the cars stored on the db ArrayList. I just really can't find an more elegant way to implement that function. Below is my current implementation:

public void showVehicles(Class<?> cls)
{
    for(Vehicle v : db)
    {
        if(v.getClass().equals(cls))
            System.out.println(v.toString());
    }
}

and invoke it like this : showVehicles(Car.class) or showVehicles(Motorcycle.class).

Is there a better method to do this? Please give your suggestions. I just feel I don't use polymorphism as it should.


Solution

  • Here is one possibility.

    I create a Map<Class<?>, List<Vehicle>> to hold the inventory.

    The map was created from a list of vehicles but it could be created by simply adding to the map outright.

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    abstract class Vehicle {
    }
    
    class MotorCycle extends Vehicle {
       String name;
    
       MotorCycle(String name) {
          this.name = name;
       }
       public String toString() {
          return name;
       }
    }
    
    class Car extends Vehicle {
       String name;
    
       Car(String name) {
          this.name = name;
       }
    
       public String toString() {
          return name;
       }
    }
    
    public class CarMapper {
    
       public static void main(String[] args) {
          List<Vehicle> list = new ArrayList<>();
          list.add(new MotorCycle("Honda"));
          list.add(new MotorCycle("Harley-Davidson"));
          list.add(new Car("Ford"));
          list.add(new Car("Chrysler"));
    
          Map<Class<?>, List<Vehicle>> inventory = list.stream().collect(
                Collectors.groupingBy(Object::getClass));
    
         }
    }
    
    

    Whether this is a reasonable approach or not is up to you (and others who comment). You may still need a map to get the correct class from the user menu.

      Map<String, Class<?>>  menuMap = new HashMap<>();
      menuMap.put("Car", Car.class);
      menuMap.put("MotorCycle", MotorCycle.class);
    
      Class<?> lookup = menuMap.get("Car");
    
      for (Vehicle v : inventory.get(lookup)) {
         System.out.println(v.toString()); 
      }