Reputation: 4637
I have a class hierarchy like below
Vehicle
|_ TransaportationVehicle has method getLoadCapacity
|_ PassengerVehicle has method getPassengerCapacity
and I have one more class Booking
it have a reference to Vehicle
.
Now whenever I have to call getPassengerCapacity
or getLoadCapacity
on vehicle
reference I need to type cast vehicle to its concrete implementation like ((PassengerVehicle)vehicle).getPassengerCapacity()
and this type of calls spans over multiple parts in the project. So is there any way with which I can avoid these type of casts and my code will look beautiful and clean?
Note: These are not actual classes I have taken these as an example to demonstrate current problem.
Upvotes: 2
Views: 874
Reputation: 379
I don't understand the example. How do you realize that you are dealing with a concrete type in the first place? Are you instanceOf-ing? Are you type matching?
If so your problem is way past casting...
Anyways when you have objects that must belong to the same family and algorithms which are not abstract and change according to the object being handled you typically use some sort of behavioral pattern like visitor, or the Bridge pattern.
Upvotes: 0
Reputation: 7649
Assuming that passenger capacity is always an integer and load capacity could very well a big number depending on what is the unit for load. I would go ahead and create Vehicle class as follow:
class Vehicle {
Number capacity;
public Number getCapacity() {
return capacity;
}
public void setCapacity(Number capacity) {
this.capacity = capacity;
}
}
The reason I am using Number
is so that I then use Integer in PassengerVehicle
class and Double in TransporatationVehicle
and that is because Integer
and Double
are subtype of Number
and you can get away with a cast.
class TransportationVehicle extends Vehicle {
@Override
public Double getCapacity() {
//all I have to do is cast Number to Double
return (Double) capacity;
}
@Override
public void setCapacity(Number capacity) {
this.capacity = capacity;
}
}
Similarly the PassengerVehicle
class as follow:
class PassengerVehicle extends Vehicle {
@Override
public Integer getCapacity() {
//Cast to Integer and works because Integer is subtype of Number
return (Integer) capacity;
}
@Override
public void setCapacity(Number capacity) {
this.capacity = capacity;
}
}
You can then use above classes to create vehicle object as follow:
public class Booking {
public static void main(String[] args) {
//
Vehicle transportationVehicle = new TransportationVehicle();
//assigning Double to setCapacity
transportationVehicle.setCapacity(new Double(225.12));
Vehicle passengerVehicle = new PassengerVehicle();
//assigning Integer to setCapacity
passengerVehicle.setCapacity(5);
System.out.println(transportationVehicle.getCapacity());
// output: 225.12
System.out.println(passengerVehicle.getCapacity());
// output: 5
}
}
On the side notes if you try to pass TransportationVehicle
anything but Number or Double then you will get Exception and similarly if you pass PassengerVehicle
anything but Number or Integer you will get exception.
I know that I am deviating from the scope of your question but, I really want to show how you can make your methods generics. This allow you to decide to return type of getCapacity()
during coding which is very flexible. See below:
class Vehicle<T> {
//generic type T
T capacity;
//generic method getCapacity
public T getCapacity() {
return capacity;
}
//generic method setCapacity
public void setCapacity(T capacity) {
this.capacity = capacity;
}
}
class TransportationVehicle<T> extends Vehicle<T> {
@Override
public T getCapacity() {
return capacity;
}
@Override
public void setCapacity(T capacity) {
this.capacity = capacity;
}
}
class PassengerVehicle<T> extends Vehicle<T> {
@Override
public T getCapacity() {
return capacity;
}
@Override
public void setCapacity(T capacity) {
this.capacity = capacity;
}
}
As you can see above the generic methods and you can use them as follow:
Vehicle<String> vehicleString = new TransportationVehicle<String>();
vehicleString.setCapacity("Seriously!"); //no problem
Vehicle<Integer> vehicleInteger = new PassengerVehicle<Integer>();
vehicleInteger.setCapacity(3); //boxing done automatically
Vehicle<Double> vehicleDouble = new PassengerVehicle<Double>();
vehicleDouble.setCapacity(2.2); //boxing done automatically
You can decide the type while coding and if you supply a Vehicle<String>
with capacity as Integer
then you will get compile time error, so you won't be allowed.
System.out.println(vehicleString.getCapacity());
//output: Seriously!
System.out.println(vehicleInteger.getCapacity());
//output: 3
System.out.println(vehicleDouble.getCapacity());
//output: 2.2
Upvotes: 0
Reputation: 51373
First try to extract an abstract method suitable for all vehicles. If you can't do this you can also use an often forgotten pattern - the visitor pattern. E.g.
Introduce a visitor interface
public interface VehicleVisitor {
public void visit(TransportationVehicle transportationVehicle);
public void visit(PassengerVehicle passengerVehicle);
}
add an accept
method to the Vehicle
public interface Vehicle {
public void accept(VehicleVisitor visitor);
}
implement the accept method in the sub classes
public class PassengerVehicle implements Vehicle {
private int passengerCapacity;
public static PassengerVehicle withPassengerCapacity(int passengerCapacity) {
return new PassengerVehicle(passengerCapacity);
}
private PassengerVehicle(int passengerCapacity) {
this.passengerCapacity = passengerCapacity;
}
public int getPassengerCapacity() {
return passengerCapacity;
}
@Override
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
public class TransportationVehicle implements Vehicle {
private int loadCapacity;
public static TransportationVehicle withLoadCapacity(int loadCapacity) {
return new TransportationVehicle(loadCapacity);
}
private TransportationVehicle(int loadCapacity) {
this.loadCapacity = loadCapacity;
}
public int getLoadCapacity() {
return loadCapacity;
}
@Override
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
implement a visitor...
public class LoadSupported implements VehicleVisitor {
private boolean supported;
private int load;
public LoadSupported(int load) {
this.load = load;
}
public boolean isSupported() {
return supported;
}
@Override
public void visit(TransportationVehicle transportationVehicle) {
int loadCapacity = transportationVehicle.getLoadCapacity();
supported = load <= loadCapacity;
}
@Override
public void visit(PassengerVehicle passengerVehicle) {
supported = false;
}
}
...and use it
public class Main {
public static void main(String[] args) {
TransportationVehicle transportationVehicle1 = TransportationVehicle
.withLoadCapacity(5);
TransportationVehicle transportationVehicle2 = TransportationVehicle
.withLoadCapacity(10);
PassengerVehicle passengerVehicle = PassengerVehicle
.withPassengerCapacity(5);
LoadSupported loadSupported = new LoadSupported(7);
supportsLoad(transportationVehicle1, loadSupported);
supportsLoad(transportationVehicle2, loadSupported);
supportsLoad(passengerVehicle, loadSupported);
}
private static void supportsLoad(Vehicle vehicle,
LoadSupported loadSupported) {
vehicle.accept(loadSupported);
System.out.println(vehicle.getClass().getSimpleName() + "[" + System.identityHashCode(vehicle) + "]" + " does"
+ (loadSupported.isSupported() ? " " : " not ")
+ "support load capacity");
}
}
The output will be something like this
TransportationVehicle[778966024] does not support load capacity
TransportationVehicle[1021653256] does support load capacity
PassengerVehicle[1794515827] does not support load capacity
Upvotes: 0
Reputation: 2073
You method overriding concepts. You need to have all these method in the Parent class and same can be overriden in the child clasees.
You can then access all the methods from super class using Runtime polymorphism
Vehicle
public interface Vehicle {
public int getCapacity();
}
TransaportationVehicle
public class TransaportationVehicle implements Vehicle {
@Override
public int getCapacity() {
return getLoadCapacity();
}
private int getLoadCapacity() {
return 0;
}
}
PassengerVehicle
public class PassengerVehicle implements Vehicle {
@Override
public int getCapacity() {
return getPassengerCapacity();
}
private int getPassengerCapacity() {
return 0;
}
}
USAGE
Vehicle passenger = new PassengerVehicle();
passenger.getCapacity();
Vehicle transaportation = new TransaportationVehicle();
transaportation.getCapacity()
Upvotes: 1
Reputation: 89169
A quick way I would accomplish this is to create a Generified Booking
parent class.
public abstract class Booking<V extends Vehicle> {
protected abstract V getVehicle();
}
public class TransportationVehicleBooking extends Booking<TransaportationVehicle> {
@Override
protected TransaportationVehicle getVehicle() {
return new TransaportationVehicle();
}
}
public class PassengerVehicleBooking extends Booking<PassengerVehicle> {
@Override
protected PassengerVehicle getVehicle() {
return new PassengerVehicle();
}
}
Your Booking
class will have all the logic that spans all the booking subclasses and some abstract method each subclasses will need to do effective calculations.
Then all you have to do is have reference to a Booking
class and calling the relevant method required without having to worry about the "logistics" (get it) of the booking itself.
I hope this helps.
Upvotes: 2
Reputation: 83849
Obviously, when booking a Vehicle
you need to distinguish at some point whether it’s a TransportationVehicle
or a PassengerVehicle
as both have different properties.
The easiest way would be to initiate two different Booking
processes: one for vehicles that can transport goods, and one for vehicles that can transport passengers. As for how to differentiate between these two types of vehicles: you could add canTransportPassengers()
and canTransportGoods()
methods to Vehicle
, the subclasses would then override these methods to return true
where appropriate. Also, this way a vehicle that can transport both is possible, like a train.
Upvotes: 3
Reputation: 1049
If You want to use different method names then You must cast to concrete class.
But if You can make this methods return same type value and have same names You can use polymorphism for it. Create abstract method in Vehicle
class and override it in each child.
Upvotes: 2