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:
Car
in myGarage[]
Vehicle myGarage[]
)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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With