Search code examples
javapolymorphismsubclassing

Calling the correct method for an object, when the object is an instance of a base class


I have the following example in Java:

public abstract class Vehicle {
    private final String name;
    private final String make;

    public Vehicle(final String name, final String make) {
        this.make = make;
        this.name = name;
    }
}

public final class Car extends Vehicle {
    public Car(final String name, final String make) {
        super(name, make);
    }
}

public final class Truck extends Vehicle  {
    final Integer grossVehicleWeight;

    public Truck(final String name, final String make, final Integer gvw) {
        super(name, make);
        this.grossVehicleWeight = gvw;
}

Say I want to do some work with a vehicle, and the work is not dependent on the subclass of vehicle. So, I have a method in another class like this:

public void doStuff(public final Vehicle vehicle) {
    //do stuff here
    //then insert it into my database:
    insertVehicle(vehicle);
}

However, I want to do different things in my insertVehicle, so I override that method for each subclass:

public void insertVehicle(Car car) { //do stuff for a car }

public void insertVehicle(Truck truck) { //do stuff for a truck }

In my doStuff method, I could use instanceOf to determine the class of the vehicle (Car or Truck), and then cast the vehicle into that class and call the insertVehicle method like this:

public void doStuff(public final Vehicle vehicle) {
    //do stuff here
    //then insert it into my database:
    if (vehicle instanceof Car) {
        insertVehicle((Car) vehicle);
    } else {
        insertVehicle((truck) vehicle);
    }
}

However, I have read that using instanceof is not the best way to do this. 1

How could I best rework this so that I do not have to use instanceof?


Solution

  • You can use the Visitor Pattern:

    public interface VehicleVisitor {
        public void visit(Car car);
        public void visit(Truck truck);
    }
    
    public class Car extends Vehicle {
    
        @Override
        public void insert(VehicleVisitor vehicleVisitor) {
            vehicleVisitor.visit(this);
        }
    }
    
    public class Truck extends Vehicle {
        @Override
        public void insert(VehicleVisitor vehicleVisitor) {
            vehicleVisitor.visit(this);
        }
    }
    
    public abstract class Vehicle {
        public abstract void insert(VehicleVisitor vehicleVisitor);
    }
    
    public class VehicleVisitorImpl implements VehicleVisitor {
    
        @Override
        public void visit(Car car) {
            System.out.println("insert car");
        }
    
        @Override
        public void visit(Truck truck) {
            System.out.println("insert truck");
        }
    }
    
    public class Main {
    
        public static void main(String[] args) {
            Vehicle vehicle = new Car();
            // finally the agnostic call
            vehicle.insert(new VehicleVisitorImpl());
        }
    
    }