Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  1. Yes, there's a Car in myGarage[]
  2. 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[])
  3. As for how to solve, there are 2 main approaches below:
    1. Use type casting, which will ease the compiler's concerns and leave any errors in the design to run time
    2. 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
  4. In many cases, composition and/or interfaces would be a better alternative to inheritance. Probably the subject of my next question...
  5. Plus many other good insights down there, if one does have the time to browse through the answers.
like image 303
T-Rex Avatar asked Jul 22 '14 09:07

T-Rex


1 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.

like image 200
Jean Logeart Avatar answered Oct 04 '22 17:10

Jean Logeart