Extending the Simple to the Slightly Complex
As previously discussed, object oriented programming (OOP) is based on the natural distribution of tasks that commonly occur in our normal world. From grocery departments to automobile specialists to the framers, plumbers, electricians, painters and so on who contribute to building houses, OOP uses this strategy to build programs that are more robust, scaleable to larger sizes, and less prone to bugs in addition to being easier to maintain.
One other natural outgrowth of OOP is the possibility of inheritance. There are plumbers who build showers and there are plumbers who lay piping under house foundations. There are electricians who pull 110 volt wiring, there are electricians that hang fixtures, and there are electricians that handle low-voltage connectivity such as TV cable, Ethernet, and low voltage lighting under the kitchen cabinets. All of these folks start with basics of being a plumber or electrician but then the extend their expertise to narrower or more focused tasks. In OOP, this is called inheritance.
In the previous section of this reference, we looked at a BoxClass. What if we wanted to extend this BoxClass to become a three-dimensional BoxClass? In Java, this is pretty easy to do, and it actually even uses the word extends to accomplish the process. Here is what that would look like:
public class BoxClassThreeD extends BoxClass
{
}
Much of the rest of the code looks like the code you would use for any other class but there are some tricks. Let's start with the normal stuff, shown here:
public class BoxClassThreeD extends BoxClass
{
// private box height
private int boxHeight;
*
*
*
You may assume that the other parts such as length and width of a "standard" box are already built in to the BoxClass, so the only thing that needs to be added here is the height, and there it is. Note that the original constants are also available from the "parent" class so we have skipped over the constant part of the class and are handling the one member variable needed to make this a three dimensional box. Now that said, there is a little issue to consider related to using the parent's data. For the most part, private data and methods may only be used within the class, so they would not be accessible to this "child" or "sub" class. That said, making the member variables public would eliminate the data hiding aspect that is important to the parent class.
So, Java has a middle of the road access modifier called protected. The protected keyword allows access from a parent class to a child class as well as other classes in the same package. This requires a little judgement. If you want to keep the data or methods hidden from the other classes in your package, you will need to use the private keyword and the child class (i.e., the BoxClassThreeD) will need to use accessors to use the parent data. However, if you aren't worried about your local (i.e., within the same package) classes inappropriately accessing these items, you can use the keyword protected. For this exercise, that is what we will do although we will show the difference a little further along on this page. The original BoxClass constants and data will be modified to protected as shown here:
public class BoxClass
{
// constant unit size default
protected final int UNIT_SIDE_SIZE = 1;
// constant default color
protected final String DEFAULT_BOX_COLOR = "Black";
// private box length and width
protected int length, width;
// private box color
protected String color:;
*
*
*
Remember that the above code is modified in the BoxClass. Nothing else needs to happen in the BoxClassThreeD once this is done.
Constructors
As discussed in the previous section, every constructor is responsible to initialize all of the member data. It turns out that this is a little easier in the case of the child class. Consider the following code:
/*
name: constructor - default
process: initializes object to default values provided as constants
uses parent constructor and sets height
method input/parameters: none
method output/parameters: none
method output/returned: none
device input/keyboard: none
device output/monitor: none
dependencies: none
*/
public BoxClassThreeD()
{
// call parent constructor
super();
// set height to unit constant
height = UNIT_SIDE_SIZE;
}
Now this is interesting. The way to get the parent's data initialized is to just call the super method. In this case, the super method has no parameters since this is a default constructor. Keep an eye out later for differences. The other variable that must be initialized is the height since it is not in the parent class but is located in the current class. By using the super method and initializing the height member variable, we have covered all of the data that must be initialized. Let's try this with an initialization constructor:
/*
name: constructor - initialization
process: initializes object to values provided as parameters
uses parent constructor and sets height
method input/parameters: length & width (int), color (String)
method output/parameters: none
method output/returned: none
device input/keyboard: none
device output/monitor: none
dependencies: none
*/
public BoxClassThreeD( int setLength, int setWidth,
int setHeight, String setColor )
{
// call parent constructor
super( setLength, setWidth, setColor );
// set height to parameter
height = setHeight;
}
Same story. Call the parent's constructor with the super method and then set the height variable. However, note this time that the parent's constructor has the three parameters that the parent's initialization constructor needs. If you think about it, that should make sense. Let's look at the copy constructor.
/*
name: constructor - copy
process: initializes object to values provided from other object
uses parent constructor and sets height
method input/parameters: copied object reference (BoxClass)
method output/parameters: none
method output/returned: none
device input/keyboard: none
device output/monitor: none
dependencies: Alternative construction call
- this( copied.length, copied.width, copied.color )
*/
public BoxClassThreeD( BoxClassThreeD copied )
{
// call parent constructor
super( copied );
// set height to copied height
height = copied.height;
}
Once again, this should be intuitive. Use the copy constructor for the parent's part and then take care of child's part that is left over.
Next up is to show a method. Since we have walked through methods in the previous section, and all the BoxClass methods were presented in the video, we will only look at one of the methods required here. However, we are going to look at the same method in two ways. The first form of the method will be provided assuming that the parent class used the access modifier protected on the constants and the member variables. This is found here:
/*
name: find3DBoxSize
process: calculates linear perimeter of the box, returns
method input/parameters: none
method output/parameters: none
method output/returned: calculated perimeter (int)
device input/keyboard: none
device output/monitor: none
dependencies: none
*/
public int find3DBoxSize()
{
// calculate, return linear perimeter
return length + width + height;
}
For the above method, the "linear perimeter" as they call it in the airlines is just the sum of the length, width, and height. For this method, the length and width are accessible from the parent since the protected keyword allows access to them within a sub, or child class. This is a little more representative of a parent/child relationship in object oriented programming and again, this is the way it will be done in the video.
However, if the class developer is concerned that the local member data of the parent might be inappropriately accessed within the local package, then she might decide to make that data private. If the programmer did that, the method would look like the following:
/*
name: find3DBoxSize
process: calculates linear perimeter of the box, returns
method input/parameters: none
method output/parameters: none
method output/returned: calculated perimeter (int)
device input/keyboard: none
device output/monitor: none
dependencies: none
*/
public int find3DBoxSize()
{
// calculate, return linear perimeter
return getLength() + getWidth() + height;
}
Again, pretty intuitive. The two accessor methods can be used without an object reference and dot operator since the class is inherited and it works just fine this way as well.
ISA vs HASA
One important consideration to point out here is the difference between inheriting a class and just using it. Our BoxClass3D is by its inheritance a BoxClass albeit one that has some extra tools. In previous sections of this reference and in the videos, you have used the Console_IO_Class to help with your I/O management. In the case where your HypotenuseClass declared and used a Console_IO_Class, it was considered to be a class that has the Console_IO_Class. Thus, the BoxClass3D ISA BoxClass because it was inherited from the BoxClass, and the HypotenuseClass HASA Console_IO_Class because the HypotenuseClass declared and used the Console_IO_Class as a member quantity. You should be familiar with this terminology as you continue working with object oriented programming.
The BoxClassThreeD has more methods. Check out this video to see how all of them go together.