Java Unit 11: Inheritance I References to Classes and Subclasses in Programs and methods
Write Superclass before Subclass The use and testing of subclasses does not differ greatly from the use and testing of any programmer written class. When creating a hierarchy of classes, you have to work your way from the top down. Subclasses can only be compiled if their superclasses exist. A test program at any level includes code that makes use of all constructors and methods in the class to make sure that they work correctly.
Test Superclass before Subclass Before starting to write a subclass, its superclass should be tested. If this is not done it is possible to encounter problems when writing and testing a subclass that are in fact caused by errors in the superclass.
Assigning a Subclass Object to a Superclass Reference When working with classes in a hierarchy, object references take on a new aspect. A reference to a subclass object can always be assigned to a reference that is declared to be of the type of any of the subclass’s superclasses. This stems from the basic idea of inheritance, that instances of classes lower down in the hierarchy are a kind of the classes higher in the hierarchy. In the same way that human beings are members of the animal kingdom, a taxed food is a kind of food.
Assigning a Subclass Object to a Superclass Reference Syntactically, this idea can be illustrated as follows: FoodV2 myFood; TaxedFoodV2 myTaxedFood = new TaxedFoodV2(“bread”, 0.50, 0.05, 0.07); myFood = myTaxedFood; This is useful in using an object in places where it’s specific subtype class can’t be used, but it’s supertype can.
Assigning a Subclass Object to a Superclass Reference A subclass object can be assigned to a superclass reference without any special syntax. This is both logically and syntactically OK. The object specifically is a taxed food item, but that does not keep it from being a food item generally, and an assignment such as this is permitted without casting.
Assigning a Subclass Object to a Superclass Reference It is important to note, however, that as soon as the reference to an object is a superclass reference, on that reference it is only possible to call the methods defined in the superclass or methods inherited by the superclass. It is not possible to call methods on a superclass reference if the methods are only defined in the subclass.
Casting a Superclass Reference back into a Subclass Object While it is possible to make an assignment from a subclass reference to a superclass reference without casting, going in the other direction is not always possible. Casting can be used in order to recover the actual class that a superclass reference belongs to.
Casting a Superclass Reference back into a Subclass Object FoodV2 myFood; TaxedFoodV2 myTaxedFood = new TaxedFood2(“bread”, 0.50, 0.05, 0.07); myFood = myTaxedFood; TaxedFoodV2 newTaxedFood = (TaxedFoodV2) myFood; This is how we get our precious subclass methods back.
Casting a Superclass Reference back into a Subclass Object In the final line, on the right, myFood is a superclass reference to a subclass object. The system internally knows what kind of object it is a reference to, and it is possible to cast that reference back to the original type and recover it in a new reference of that type.
Can’t cast to types outside the hierarchy This type of casting will not work if the type on the left is not the same type as the actual object on the right. Here is a gross example of something that is simply NOT possible: //NOOOOO! Ellipse2D.Double myEllipse = new Ellipse2D.Double(10, 20, 30, 40); TaxedFoodV2 myTaxedFood = (TaxedFoodV2) myEllipse; It should be obvious that it is illogical to try to cast an ellipse to a food.
Can’t cast to types outside the hierarchy Product Food Ketchup Waffles NonFood Toothpaste Cat Toy
Can’t cast superclasses to subclasses The following is equally impossible: /* NO! NO! NO! */ FoodV2 myFood = new FoodV2(“cheese”, 1.25, .06); TaxedFoodV2 myTaxedFood = (TaxedFoodV2) myFood; Even though myFood and myTaxedFood are in the same inheritance hierarchy, in this example myFood never was a taxed food. It is impossible to use casting to recover a kind of reference that never existed. As a general rule, it is not possible to cast superclasses to subclasses, except in those cases where you are recovering an original reference type.
‘instanceof’ Keyword This introduces another keyword that allows you to test whether a reference belongs to a particular class before trying to cast it. The keyword instanceof functions as an operator that returns a boolean value. It compares the actual type of a reference to a given class.
‘instanceof’ Keyword The fragment below illustrates its use to permit a valid cast and prevent a mistaken one: if (myFood instanceof TaxedFoodV2) { TaxedFoodV2 newTaxedFood = (TaxedFoodV2) myFood; }
Passing Object References as Parameters The question of reference type becomes critical when passing object references as parameters to methods in an inheritance hierarchy. A subclass object can always be given a superclass reference. What follows from this is the idea that when a superclass reference is specified, it is permissible to use a subclass object or reference in its place. Remember System.arraycopy() ?
Passing Object References as Parameters Here is a new method for use in the food class. Let this be the next modification of the example, so that the class names now include V3 at the end. It is possible to set the markup of one food item to the markup already stored in another. This is accomplished by passing the other object as a parameter to the method. The parameter is typed FoodV3, the same as the (super) class the method appears in.
Passing Object References as Parameters public void setMarkuptToThisMarkup(FoodV3 anotherFood) { double tempMarkup = anotherFood.getMarkup(); setMarkup(tempMarkup); }
Passing Object References as Parameters Now suppose the following code exists in a program that uses this set of classes: TaxedFoodV3 myTaxedFood = new TaxedFoodV3(“bread”, 0.50, 0.05, 0.07); TaxedFoodV3 yourTaxedFood = new TaxedFoodV3(“milk”, 0.75, 0.08, 0.07); myTaxedFood.setMarkupToThisMarkup(yourTaxedFood);
Passing Object References as Parameters Is the call in the third line correct? The method is inherited by the subclass, so making the call on myTaxedFood is possible. The apparent problem is that the type of the explicit parameter, yourTaxedFood, is TaxedFoodV3, but in the method definition the expected parameter is typed FoodV3. In other situations where the actual parameter type did not agree with the formal parameter type, the compiler detected an error. It’s like assigning a TaxedFood to a Food type.
Passing Object References as Parameters However, in this case there is not a problem. The system knows that TaxedFoodV3 is a subclass of FoodV3, and it does automatic type conversion. When the actual parameter, a taxed food object, is passed, that reference is automatically converted to a FoodV3 reference. This is possible because a superclass reference to a subclass object is allowed.
New Food classes The changes made to FoodV3 and TaxedFoodV3 will be found on our website.