Do I really have a car in my garage? [duplicate]
I'm a newbie to Java programming, trying to get the hang of OOP.
So I built this abstract class:
public abstract class Vehicle{....}
and 2 subclasses:
public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}
Car
and Boat
also hold some unique fields and methods that aren't common (don't have the same name, so I can't define an abstract method for them in Vehicle).
Now in mainClass I have setup my new Garage:
Vehicle[] myGarage= new Vehicle[10];
myGarage[0]=new Car(2,true);
myGarage[1]=new Boat(4,600);
I was very happy with polymorphism until I tried to access one of the fields that are unique to Car, such as:
boolean carIsAutomatic = myGarage[0].auto;
The compiler doesn't accept that. I worked around this issue using casting:
boolean carIsAutomatic = ((Car)myGarage[0]).auto;
That works... but it doesn't help with methods, just fields. Meaning I can't do
(Car)myGarage[0].doSomeCarStuff();
So my question is - what do I really have in my garage? I'm trying to get the intuition as well as understand what's going on "behind the scenes".
for the sake of future readers, a short summary of the answers below:
- Yes, there's a
Car
inmyGarage[]
- Being a static typed language, the Java compiler will not lend access to methods/fields that are non-"Vehicle", if accessing those through a data structure based on the Vehicle super class( such as
Vehicle myGarage[]
) - As for how to solve, there are 2 main approaches below:
- Use type casting, which will ease the compiler's concerns and leave any errors in the design to run time
- The fact that I need casting says the design is flawed. If I need access to non-Vehicle capabilities then I shouldn't be storing the Cars and Boats in a Vehicle based data structure. Either make all those capabilities belong to Vehicle, or use more specific (derived) type based structures
- In many cases, composition and/or interfaces would be a better alternative to inheritance. Probably the subject of my next question...
- Plus many other good insights down there, if one does have the time to browse through the answers.
If you need to make the difference between Car
and Boat
in your garage, then you should store them in distinct structures.
For instance:
public class Garage {
private List<Car> cars;
private List<Boat> boats;
}
Then you can define methods that are specific on boats or specific on cars.
Why have polymorphism then?
Let's say Vehicle
is like:
public abstract class Vehicle {
protected int price;
public getPrice() { return price; }
public abstract int getPriceAfterYears(int years);
}
Every Vehicle
has a price so it can be put inside the Vehicle
abstract class.
Yet, the formula determining the price after n years depends on the vehicle, so it left to the implementing class to define it. For instance:
public Car extends Vehicle {
// car specific
private boolean automatic;
@Override
public getPriceAfterYears(int years) {
// losing 1000$ every year
return Math.max(0, this.price - (years * 1000));
}
}
The Boat
class may have an other definition for getPriceAfterYears
and specific attributes and methods.
So now back in the Garage
class, you can define:
// car specific
public int numberOfAutomaticCars() {
int s = 0;
for(Car car : cars) {
if(car.isAutomatic()) {
s++;
}
}
return s;
}
public List<Vehicle> getVehicles() {
List<Vehicle> v = new ArrayList<>(); // init with sum
v.addAll(cars);
v.addAll(boats);
return v;
}
// all vehicles method
public getAveragePriceAfterYears(int years) {
List<Vehicle> vehicules = getVehicles();
int s = 0;
for(Vehicle v : vehicules) {
// call the implementation of the actual type!
s += v.getPriceAfterYears(years);
}
return s / vehicules.size();
}
The interest of polymorphism is to be able to call getPriceAfterYears
on a Vehicle
without caring about the implementation.
Usually, downcasting is a sign of a flawed design: do not store your vehicles all together if you need to differenciate their actual type.
Note: of course the design here can be easily improved. It is just an example to demonstrate the points.
To answer your question you can find out what exactly is in your garage you do the following:
Vehicle v = myGarage[0];
if (v instanceof Car) {
// This vehicle is a car
((Car)v).doSomeCarStuff();
} else if(v instanceof Boat){
// This vehicle is a boat
((Boat)v).doSomeBoatStuff();
}
UPDATE: As you can read from the comments below, this method is okay for simple solutions but it is not good practice, particularly if you have a huge number of vehicles in your garage. So use it only if you know the garage will stay small. If that's not the case, search for "Avoiding instanceof" on stack overflow, there are multiple ways to do it.