简体   繁体   中英

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 db together 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 eg 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.

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

Actually this is not the way an object-oriented approach is usually used. Here is an example with polymorphic approach. The 'printName' function is defined in an abstract class (could be an interface in this case as well):

import java.util.ArrayList;

abstract class Vehicle {
    public abstract void printName(); // virtual function
}
class Car extends Vehicle {
    public void printName() { // function implementation in the class Car
        System.out.println("I am a car");
    }
}
class Bike extends Vehicle {
    public void printName() { // function implementation int the class Bike
        System.out.println("I am a bike");
    }
}


public class T1{
    ArrayList <Vehicle> vehicles = new ArrayList<Vehicle>();

    void create() {
        vehicles.add(new Car());
        vehicles.add(new Bike());
    }
    void list() {
        for (Vehicle v: vehicles)
            v.printName();  // use the function at the show time
    }
    public static void main(String args[]) {
        T1 t1 = new T1();
        t1.create();
        t1.list();
    }
}

There will be a big difference if you want to use a real database. The latter can only save data fields from a java class and restore them into the class. So, your application must know about the classes at read time and make sure that correct classes are filled with correct data. Serialization can help in this case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM