
How to do it...
- Create the Engine class as an inner class of the Vehicle class:
public class Vehicle {
private int weightPounds;
private Engine engine;
public Vehicle(int weightPounds, int horsePower) {
this.weightPounds = weightPounds;
this.engine = new Engine(horsePower);
}
public double getSpeedMph(double timeSec){
return this.engine.getSpeedMph(timeSec);
}
private int getWeightPounds(){ return weightPounds; }
private class Engine {
private int horsePower;
private Engine(int horsePower) {
this.horsePower = horsePower;
}
private double getSpeedMph(double timeSec){
double v = 2.0 * this.horsePower * 746 *
timeSec * 32.17 / getWeightPounds();
return Math.round(Math.sqrt(v) * 0.68);
}
}
}
- Notice that the getSpeedMph(double timeSec) method of the Vehicle class can access the Engine class, even though it is declared private. It can even access the getSpeedMph(double timeSec) private method of the Engine class. And the inner class can access all private elements of the enclosing class, too. That is why the getSpeedMph(double timeSec) method of the Engine class can access the private getWeightPounds() method of the enclosing Vehicle class.
- Look closer at the usage of the inner Engine class. Only the getSpeedMph(double timeSec) method of the Engine class is used. If the designer believes that it is going to be the case in the future too, they could reasonably decide to make the Engine class a method-local inner class, which is the second type of an inner class:
public class Vehicle {
private int weightPounds;
private int horsePower;
public Vehicle(int weightPounds, int horsePower) {
this.weightPounds = weightPounds;
this.horsePower = horsePower;
}
private int getWeightPounds() { return weightPounds; }
public double getSpeedMph(double timeSec){
class Engine {
private int horsePower;
private Engine(int horsePower) {
this.horsePower = horsePower;
}
private double getSpeedMph(double timeSec){
double v = 2.0 * this.horsePower * 746 *
timeSec * 32.17 / getWeightPounds();
return Math.round(Math.sqrt(v) * 0.68);
}
}
Engine engine = new Engine(this.horsePower);
return engine.getSpeedMph(timeSec);
}
}
In the preceding code example, it does not make sense to have an Engine class at all. The speed-calculation formula can be just used directly, without the mediation of the Engine class. But there are cases when this might be not so easy to do. For example, the method-local inner class may need to extend some other class in order to inherit its functionality, or the created Engine object may need to go through some transformation, so creation is required. Other considerations may require a method-local inner class.
In any case, it is a good practice to make all the functionality that is not required to be accessed from outside the enclosing class inaccessible. Encapsulation—hiding the state and behavior of objects—helps avoid unexpected side effects resulting from an accidental change or overriding object behavior. It makes the results more predictable. That's why a good design exposes only the functionality that must be accessed from the outside. And it is usually the enclosing class functionality that motivated the class creation in the first place, not the inner class or other implementation details.