Chapter 9

Inheriting Code and Data in Java

You’ve come a long way already in this book. In this chapter and the next two, you get the payoff: learning how to inherit code, create so-called pure types, and examine a type system. As I cover each of these topics, you’ll get your first look at moving from writing programs that happen to be Java classes to writing classes that can be used to build multiple programs.

It’s a challenging step to move from writing code you need right now to writing code you can reuse. It is a practice that takes time to master. You’ll start in this chapter by understanding how you can extend code that’s already been written.

In this chapter, I’ll cover the following topics:

  • Extending a class
  • Using the super keyword
  • Overriding a method
  • Casting objects
  • Implementing polymorphic behavior

Extending a Class

certobjective.eps

Ten to 15 years ago, the author of a Java primer would probably have made a big splash over the benefits of inheriting code—best thing since sliced bread and all that. Say you happen across a complicated but useful class. You want to build on it, but also keep the existing code intact. In Java, all you have to do is apply the extends keyword, like this:

public final class MyAction extends Thread { 
// add your program code here
 }

The Thread class lives in the java.lang package.

When you use the keyword extends and name a Java class like Thread, the MyAction class becomes a version of the Thread class. In that sense, your average author from long ago could then exclaim how powerful a feature inheritance is—using the word power, in this case, as a synonym for convenience. By creating objects of type MyAction, you can invoke any visible method the Thread class contains as if it belongs to the subclass, which it does.

This property of the language is a powerful lure. Most people are tempted (and taught) to apply it well before they understand all the trade-offs of its use, and while those trade-offs do not foretell the death of any program, they do not go away easily either. In this chapter, then, I will focus on when and when not to use inheritance. It does you no good to learn about the problems you can create for yourself after you’ve written an important program. Years of practice have taught us all that thinking “inheritance first” can create as many problems as it’s been used to solve.

Understanding What Inheritance Provides

What does inheritance give you? For one, it makes all the accessible members of the parent class available to the child class. This set includes all fields and methods declared public or protected. If the child class resides in the same package as the parent, it will also see the parent’s default-access members.

A subclass does not inherit, nor does it have any special access to, the private members of the parent class. It also does not inherit methods and fields that are declared static, but the reasons why are different. Members that are declared private are internal elements of a class, available only to objects of that kind. A static member can be made accessible to anyone, but it is rooted in the class that declared it.

It seems like you could emulate inheritance, if you wanted to, by wrapping every available method of a class that you declared as a field. For example, if the MyAction class declared a Thread member instead of extending the class, it could include a run() method that simply calls the run() method of the Thread field, like this:

public final class MyAction {
private Thread thr = new Thread();
public void run() {
thr.run();
   }
public void start() {
thr.start();
   }
// wrap remaining methods of Thread
}

The run() method in the Thread class has no code, so it won’t do anything observable if you call it.

This MyAction class uses a thr reference to invoke the Thread class’s run() and start() methods. I could go on to wrap every other accessible method, but even if I did, this version of the MyAction class wouldn’t work quite the same as a Thread subclass. It’s that difference I want you to learn from this chapter.

First, there is a difference in access rights. The protected keyword provides default access that also includes subclasses in other packages. If you want to wrap a number of methods from a class in a different package, you won’t have access to any methods that are declared protected. If you extend that same class, access to all protected methods is included. You may have wondered, “Why bother?” when you first came across the protected keyword, but without it, a parent class cannot share its members with subclasses outside its package.

Second, when you extend the Thread class, the compiler and the JVM can infer its type as a part of the MyAction class. With a wrapping technique, you can hide (or encapsulate) one class’s relationship to other classes. Two wrapper classes can have exactly the same data and methods, and the compiler will never assume they are related. A class that extends the Thread class, however, is always that type of object. That means if you have a method that requires a Thread parameter, you can pass it a MyAction object instead, and the compiler will always accept it.


You can also refer to inheritance as an is-a relationship and composition as a has-a relationship.

This distinction in relationships is important enough that you will diagram the two in different ways. Figure 9-1 illustrates an inheritance relationship on the left and a composition relationship on the right.

Figure 9-1: Extending and incorporating the Thread class in the MyAction class

c09f001.eps

In the Unified Modeling Language (UML), an unfilled arrowhead with a solid line pointing from one class to another, implies inheritance. The pointing class captures the attributes of the pointed-to class. In UML terms, the pointing class, which we call a child, specializes the one we call a parent. Unfortunately for beginners, UML defines this relationship from the bottom up and instead calls it a generalization. That is, the child class is generalized by its parent. Since UML is concerned with code modeling, not implementation, it describes class relationships in terms that favor a designer’s point of view.

Extending the Parent Class

Once you add code and data to a new subclass, you apply the full meaning of extending the parent. In composition, you add code and data to create a new class that is specific to your needs. When you extend a class, you’re doing the same thing but with a specific type for a base. In this example, we’re creating a type that specializes the Thread class.

Specializing simply means that you add members to the MyAction class to further refine or elaborate on its Thread capability. If you decide a MyAction class should walk as well as run, you can just add a method called walk():

public final class MyAction extends Thread {
public void walk() {
// “walk” the code instead of running it
   }
}

This is a purely figurative example, of course. The run() method in the Thread class doesn’t do anything “fast,” it just executes code that’s been supplied to it. But the idea of adding fields and methods to a subclass is important. When you add new operations to a child class, you express how it differs from its parent and expresses a new type.

Some subclasses don’t do this at all, opting instead to change the behavior provided by the parent class. This is done by overriding one or more parent methods. It’s loosely similar to method overloading. In method overloading, you change the parameter list of an existing method to supply an alternate version of it. In overriding, a child class overrides an inherited method by duplicating its method name and parameter list, then changing the code. I’ll review method overriding in detail later in this chapter.

Removing the final Keyword

When you define a class with the final modifier, you declare it ineligible for subclassing. Remove the modifier, recompile the code, and voilà! You can then use the extends keyword and inherit from that class. I’ve used this modifier since Chapter 2, “Applying Data Types in Java Programming,” to signify that the classes I’ve written aren’t intended as parent types. It doesn’t mean they’re unsafe, or ineligible, or somehow incomplete. It’s my way of saying that a class shouldn’t allow inheritance unless it has been designed to support it. The absence of the final keyword, in my view, should signal that it’s safe, not just legal, to inherit from the class at hand. If you leave the final modifier off of your class declarations, I’d go further and say you’re promoting your class as a parent type. Inheritance is such a powerful facility that, if I had designed the language, I’d have made it necessary to declare a class eligible as a parent.

Here’s one reason why: A subclass can’t access any parent class private member, including constructors. That means inheritance isn’t absolute; it relies on exposing at least one constructor to its subclasses. You can therefore break an existing child class just by changing visibility in the parent class. The simplest example of this condition looks like this:

public class Parent {
public Parent() {}
}
final class Child extends Parent { }

This Parent class is just a shell that the Child class extends. Let’s say, for the sake of illustration, that you’re using both classes in some program. For some reason, you decide the access modifier of the Parent() constructor should be private. Then you recompile the Parent class. Then you try to compile the Child class and find out it won’t.

You had a working Child class, you didn’t touch it, and now it’s broken. It won’t help, by the way, to avoid recompiling the Child class; you’ll just see the problem at runtime instead of compile time. What happened? The error message (“Parent() has private access in Parent”) won’t help unless you already know what’s going on. The short answer is that a subclass depends on its parent class for construction. You cannot encapsulate the Parent class in a way that protects the Child class from such changes.

This outcome illustrates one reason for using the final modifier. You can use it to mean, as I usually do, that the class members are not yet stable enough that making subclasses will be safe.

Observing the Equality Contract

A less obvious reason for using the final keyword has to do with what’s called the equality contract, which is related to the semantic difference between the == operator and the equals() method.

In Chapter 3, “Using Java Operators and Conditional Logic,” you learned that the equals() method in the Object class wraps the == operator. You also learned that subclasses like String alter this method to provide a more permissive test, one that compares the content of two String referents. The strict test, whether two references point to the same referent, is the strictest possible case for that test.


The rules for overriding the equals() method are documented at http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object).

To change a method in a subclass, you have to override it. You’ll learn how to do that later in this chapter. Changing the equals() method is just one step. As the documentation will tell you, there are five requirements you should observe if you want to change the way equality works for your class. And while it’s always safe to leave the equals() method as it is, it’s usually too strict to be useful.

A non-final class that changes the equals() method but doesn’t observe the remaining criteria might be brittle. Its subclasses will inherit that brittleness without knowing about it and will assume that the equals() method is both reliable and useful. And that’s another reason to use the final keyword: to signify that a class does not fulfill the equality contract and probably shouldn’t be subclassed until it does.

It’s worth noting there are several benefits in using the final modifier too. For one, the compiler can convert a final class to bytecode in a way that improves its performance traits over non-final classes. You should not expect another programmer to infer your intentions when using this modifier. Take time to add comments to your code and remove any doubts.

Observing Java’s Single Inheritance Rule

There’s one more thing to consider when choosing inheritance or composition to implement a new class. Java lets you inherit from one parent class only. Every class in Java has only one direct is-a relationship.

Languages like C++ allow programmers to inherit from multiple classes as they see fit. Like any powerful feature in any language, multiple inheritance is easy to misuse if you don’t think through its consequences before you apply it. Java’s designers felt the potential harm of this feature outweighed its benefits and chose to disallow it.

If you do choose to extend another class, know that you are fixing the ancestry of the new class to its parent and its super-parents, if any. A new class will always inherit something from the Object class, but the most important relationship should be with its most immediate parent.

Ask yourself if a class you want to extend fulfills all of the following criteria:

  • It implements the equality contract.
  • It provides one or more methods that will save the subclass a lot of work.
  • You will add to or modify the inherited code and data to specialize its operations.

If all of these tenets hold, you can justify inheritance over composition. If they don’t, ask yourself if you’re just counting on inheritance to save you time. This convenience always seems attractive in the early stages of writing a program but often loses its charm once the first few problems with coordinating your objects set in. When in doubt, remember that it’s usually easier to change your mind and switch to subclassing than it is to switch to composition.

Understanding How Inheritance Works

Every class in Java inherits from the Object class, but how is that enforced? How can you observe the relationship between a class and the Object class? More to the point, how can you observe the relationship between any class and its parent class or subclasses?

In Java, it’s the constructor that manages this relationship. Every time you write a new Java class, the compiler tests whether you may inherit from the parent you have claimed. It does this by ensuring that each constructor can call a valid constructor in the parent class. You can demonstrate this linkage by making the parent constructor do something observable, as shown in Listing 9-1.

Listing 9-1: Demonstrating inheritance among three related classes

public class Grandparent {
public Grandparent() {
      System.err.println("Construct Grandparent");
   }
}
class Parent extends Grandparent {
public Parent() {
      System.err.println("Construct Parent");
   }
}
final class Child extends Parent {
public Child() {
      System.err.println("Construct Child");
   }
public static void main(String args[]) {
// Create one Child object; the rest
      // takes care of itself.
      Child ch = new Child();
   }
}
$ javac Grandparent.java
$ ls *class
Child.class   Grandparent.class   Parent.class
$ java Child
Construct Grandparent
Construct Parent
Construct Child

Notice in Listing 9-1 that each class inherits from the class before it. To allow this, I removed the final keyword from the Grandparent and Parent class definitions. I also added to each class a no-arg constructor that will print a message when it’s called. The Child class contains a main() method that constructs only a Child object.

The output shows that before the Child object is constructed, a Parent object is constructed, and before that, a Grandparent object is constructed. If you could modify the Object class code to follow suit, you’d see its output first. Thus the construction of any object requires the construction of its parent objects first.

In one sense, this process demonstrates that a Child class is a Parent type, which is a Grandparent type (which is an Object type). It also shows that an accessible constructor is essential to permitting inheritance. In another sense, it explains why a variable of any parent type can refer to a Child referent.

Although it confuses our terms to say a Child class is composed of its parent classes, technically it’s not that far off the mark. Composition and inheritance are not opposing techniques in Java, although they are often presented as such. They are complementary techniques—two ways of achieving the same goal, which is to reuse existing code.

The parent-first construction model suggests that inheritance occurs by layering new class members on top of the ones declared by parent classes. The compiler and JVM validate and construct the parent classes first. When they arrive at the Child class, they can then ascertain if that class either reuses or overrides the inherited members. I will expand on that point later in the chapter, in the section “Overriding a Method.”

Listing 9-2 shows how one class can defeat subclassing without using the final keyword.

Listing 9-2: A broken inheritance relationship

public class Paternity {
public Paternity() {}
}
class Parent extends Paternity {
public Parent(int xy) {}
}
class Child extends Parent {}
class Lab {
   Parent sample = new Parent(25);
}
$ javac Paternity.java
Paternity.java:11: error: constructor Parent in class Parent 
   cannot be applied to given types;
class Child extends Parent {}
^
  required: int
  found: no arguments
  reason: actual and formal argument lists differ in length
1 error

In Listing 9-2, the Paternity class has one no-arg constructor. The Parent class subclasses Paternity and supplies a constructor that requires an integer. The Child class extends Parent. The Lab class declares and constructs a Parent member.

The Lab class, which constructs a Parent instance, appears to work fine. The Child class throws an error. The output is trying to tell us the Child constructor must also provide an integer. The chain of inheritance breaks because the Child class does not adhere to the same rules as any class that, like the Lab class, passes the Parent class what it needs to construct.

There are only two ways to manage this problem. You can remove the extends declaration from the Child class and use composition with the Parent class, just as the Lab class does. Or, you can pass a parameter to the Parent class through a Child constructor. If only you knew how.

Using the super Keyword

certobjective.eps

When you use the final keyword, you intend to prevent inheritance. If you’re not careful, requiring parameters in a parent class’s constructor can have the same effect. Now that you know Java uses constructors to confirm inheritance, you need a way to pass information through them from any subclass to its parent class. The super keyword, treated as a constructor call, supports this need.

Passing Parameters to a Parent Constructor

Does the constructor linkage I described in the preceding section seem like a bit of magic? It’s not anything of the sort. It’s just another bit of behind-the-scenes work in Java. The compiler happens to inserts a super() call in every constructor. You won’t see it in source code, but if you learn to read the bytecode a compiler produces, you will. It is always the first call made in a constructor’s statement body. Like other implicit class elements in Java, you’re not required to include it, but you can. The compiler won’t like it only if you try to move it around, however, as shown in Listing 9-3.

Listing 9-3: Using the super() call in a constructor

public class NewParent {
}
class GoodChild extends NewParent {
public GoodChild() {
super();
int x = 1;
   }
}
class BadChild extends NewParent {
public BadChild() {
int x = 1;
super();
   }
}
$ javac NewParent.java
NewParent.java:14: error: call to super must be first statement 
   in constructor
      super();
           ^
1 error

In Listing 9-3, the GoodChild and BadChild classes each extend the NewParent class. Their constructors do the same work but in different order. The compiler’s error message is plain enough; we can trace the line number (14) back to the use of super() in the BadChild class. The GoodChild class shows we can make the call explicit so long as it adheres to the rule.

The super() invocation calls the no-arg constructor in the NewParent class, whether it’s supplied by the compiler or the programmer. If a parameter is required by the parent class, making the call explicit wouldn’t help; you would also have to supply parameters just as you would with a method.

If you rewrite the Child class in Listing 9-2 with this advice in mind, you can compile it without complaint. Here’s the new version of that Child class and the results of compilation:

class Child extends Parent {
public Child() {
super(0);
   }
}
$ javac Paternity.java
$

As always, the compiler doesn’t know or care if the value you supply is meaningful to the parent class. It only cares that the value’s type passes legal muster.

Choosing a Parent Constructor

If a parent class has multiple constructors, each subclass can use the appropriate super() call to select the constructor it wants to use. It’s just like calling a specific constructor using the new keyword and the correct parameters. Listing 9-4 shows an Option class with overloaded constructors and subclasses that use different ones.

Listing 9-4: Class with multiple constructors and subclasses

public class Options {
private String str = "default";
public Options() {}
public Options(String str) {
this.str = str;
   }
public Options(char [] chars) {
this.str = new String(chars);
   }
}
class Default extends Options {}
class OptionB extends Options {
public OptionB() {
super("OptionB");
   }
}
class OptionC extends Options {
private static char[] name =
      { 'O', 'p', 't', 'i', 'o', 'n', 'C' };
public OptionC() {
super(name);
   }
}

The Options class has three constructors. Their net result is to assign the member str the value "default" unless a String or character array parameter is passed in. The Default class uses the no-arg constructor, while the OptionB and OptionC classes pass in the text of their class names using different types.

You can use overloaded constructors to spare the caller the effort of converting their data to a type required by one constructor. There may be other good cases as well. A class that varies its constructors’ behavior with different parameters is still a bad idea, just as it is with overloaded methods.

Overriding a Method

certobjective.eps

In Chapter 7, “Using Java Methods to Communicate,” you learned the rules of overloading. You can write the same method (or constructor) with different parameter lists, giving the caller more flexible ways to communicate. Allowing for multiple parameter types is one reason to support overloading, but you might also want to let the caller provide varying amounts of information. I didn’t show this second case with the constructor example earlier, but it works the same way as it would with methods.

Method overriding is similar in appearance to overloading, but its action and purpose are quite different. On first view, it appears to have the same effect as wrapping a method call from another class, as shown in Listing 9-5.

Listing 9-5: Wrapping a String class method

public final class StringWrap {
private String str = "wrapper";
public String toUpperCase() {
return str.toUpperCase();
   }
public static void main(String args[]) {
      StringWrap sw = new StringWrap();
      System.out.println(sw.toUpperCase());
        }
}

$ javac StringWrap.java
$ java StringWrap
WRAPPER

In the StringWrap class, I duplicated the signature of the String.toUpperCase() method and wrapped a call to it, passing its work off as my own. I could also have decorated its behavior with additional logic or even changed it altogether. Whatever I do, I want my toUpperCase() method to look the same. I want it to be familiar to programmers who know the String class, and I want them to have similar expectations for using this method.


Most member variables are encapsulated, so they aren’t visible to subclasses.

In a subclass, you only have to redeclare a member you inherited. The compiler silently overrides or replaces the inherited member from the parent class with your version. It doesn’t matter which parent class the method appears in, but you will override the one in the most immediate parent class. Overriding is how you change the behavior of the equals() method in the Object class or any other member along the way.

There are a couple rules to follow, some required and some that are just good advice. For one, you must match the signature of the inherited method. If you don’t—if you get the parameter list wrong, for example—you will overload the method instead. There’s no rule that says you have to overload a method in the same class. There’s nothing wrong with overloading an inherited method, but it can be a frustrating thing to track down.


This visibility rule does not apply to member variables.

Also, you may not restrict the visibility of an inherited method. That is, you cannot override an inherited public method and declare it to have protected, default, or private access. The compiler will not interpret this modifier change as an attempt at overloading; it will simply reject it. You can, however, change the visibility from a more restrictive to a more open modifier.

Using the @Override Annotation

There is a third rule that isn’t required but goes a long way to clarifying your code: Use the @Override annotation. The compiler accepts correct method overriding and overloading silently, so it’s not necessarily clear which technique you’ve applied, as shown in Listing 9-6.

Listing 9-6: Mistaking method overloading for method overriding

public class MyMethod {
   String str = "Philadephia";
public void engage(String str) {
      System.out.println(str);
   }
}
final class Override extends MyMethod {
// @Override
public String engage() {
      return this.str;
   }
public static void main(String args[]) {
      Override sc = new Override();
// the method you intended to override:
// sc.engage("Philadephia");
      System.out.println(sc.engage());
   }
}
$ javac MyMethod.java
$ java Override
Philadephia

The MyMethod class has an engage() method that accepts a String parameter to print. The Override class also has an engage() method, but it returns a reference to the String variable it inherited from the MyMethod class.

For the sake of argument, let’s say the MyMethod class is located in a different file. It’s late and you’re tired. You’re pretty sure you remember the engage() method’s signature, so you write the Override class as a test and persuade yourself from the output that your version of the engage() method is an override. You call it a day, and maybe a couple days later you pick up where you left off.

It could be a long time before you figure out you’ve actually overloaded the engage() method instead of overriding it. If you forget why you wrote the Override class to begin with, it would be hard to deduce that your output was a fluke, aided by an object field you normally would not make visible.

Of course you could cut through the problem by looking up the MyMethod class code and seeing your mistake. It would be quicker, however, just to apply the @Override annotation to the failed override and let the compiler tell you what’s up. If you uncomment that line in Listing 9-6, you’ll see this output:

$ javac MyMethod.java
MyOverride.java:9: error: method does not override or 
   implement a method from a supertype
   @Override
   ^
1 error

Apply this annotation any time you want to override a method and you will turn the compiler’s gift for complaining to your advantage.

Modifying or Replacing an Inherited Method

Sometimes you only want to add to the code you receive from a parent method. Or, possibly, there are cases in which a parent’s method code is more appropriate than yours. In either event, you can use the super keyword as an object reference to the parent implementation. Listing 9-7 shows code that will use the Object class’s toString() method if a user passes the null value to the constructor.

Listing 9-7: Using the super keyword to access a parent method

public final class SuperTest {
private String str;
public SuperTest(String str) {
this.str = str;
   }

   @Override
public String toString() {
      if (this.str == null) {
         System.err.print("Null value: called Object.toString(): ");
return super.toString();
      }
return this.str;
   }
public static void main(String args[]) {
      SuperTest sm1 = new SuperTest(null);
      SuperTest sm2 = new SuperTest("");
      SuperTest sm3 = new SuperTest("hello");
      System.out.println(sm1);
      System.out.println(sm2);
      System.out.println(sm3);
   }
}
$ javac SuperTest.java
$ java SuperTest
Null value: called Object.toString(): SuperTest@437dcb

hello

Remember, there is a difference between a reference that points to an empty object and a reference that points to null.I

The toString() method in the SuperTest class tests the member variable str. If it is equal to null, the method calls the parent version of the toString() method. Otherwise it returns the value of its member variable. I also added a System.err.print() method to explain what caused the call to the parent method.

You can’t call the parent’s parent method using this technique (or any other in Java). Just as in real life, sometimes the methods your parent class has don’t look as good as the ones it inherited. Still, there’s no way around your immediate parent. In the Java language, this relationship is a condition your subclass must observe, not a problem it can solve.


It’s good advice to override the toString() method in all cases. Almost anything is better than the Object class’s implementation.I

To completely replace a method you’ve inherited, just redeclare it and write your own logic. The compiler requires your override to observe only the parameters and return type supplied. Although it does not enforce the meaning or the effect originally intended by the method, it’s expected that you won’t deviate from what other programmers expect the method to achieve. It’s your obligation to support those expectations.

This requirement may sound like an obvious bit of wisdom. However, in an object-oriented language, naming matters. Names convey semantic meaning. If you pick good names and remain consistent with their interpretation, a calling programmer can learn those meanings and apply them to any number of related classes without researching them for each case.

If you preserve these meanings well as you go about implementing methods, you can achieve what’s called a polymorphic effect among your related classes. This effect is a very big deal when it’s done well. I’ll address the basics of polymorphic effect in the final section of this chapter.

Prohibiting Method Overrides

Maybe your method logic has been implemented to perfection for all time and satisfies all cases in the known universe. Or maybe its operation depends completely on the object state of its host class, so much so that it would break in strange ways if it were overridden. Whatever the case might be, you sometimes have to protect certain methods from change. You have three options, each with differing effects.

Reduce your method’s visibility. You can make a method visible only to its class, only to its package, or only to its package and subclasses, whichever makes sense. This approach limits the range of subclasses that can modify it. This approach prohibits overriding only in the sense of making the parent method logic inaccessible outside the scope you allow.

If a subclass declares a method that has the same signature as a parent’s private method, the compiler will allow it. It’s not an override, however; the parent method is still inaccessible. It’s a consequence of the rules for each class’s namespace. The names of private members in a parent class are not restricted from any subclass’s namespace.


The static members of a class load before objects can be constructed and therefore cannot refer to them.

Associate your method with its class. If you declare a method static, the method is bound to its host class and cannot be inherited. If the method is designed to behave like a function—a method that returns some result but does not affect object state—using the static modifier is a good choice. If the method is supposed to change object state, using the static modifier defeats that purpose.

If a subclass declares a method with the same signature as a parent’s static method, minus the modifier, the compiler will not allow it. An accessible static method in the parent class does affect the namespace of the subclass. This approach prohibits overriding by way of visibility. The parent method is bound to its class but it is not hidden. You cannot override a static method in a subclass.

Declare your method closed to overriding. Finally, you can just apply the final modifier to a method, even if its class is declared final too. If you want to open a class to inheritance but protect certain methods from overriding, declare those methods final. It is the clearest, most direct approach and has no strange conditions or side effects. Unlike the first two options, declaring a method final renders the method name off limits to all subclasses for all cases.

Casting Objects

certobjective.eps

In Listing 9-1, I showed an inheritance relationship among the Grandparent, Parent, and Child classes. The code demonstrates how the order of construction plays a role in forming any new referent. It’s as if the parent referents provide a core on which the child referents rely, as shown in Figure 9-2.

Figure 9-2: Conceptual view of a subclassed referent

c09f002.eps

In a real sense, all four of these types shown occupy the area in memory that stores the referent. You can demonstrate this idea by using any parent type to refer to the actual object. Modify the main() method in Listing 9-1 as shown here:

public static void main(String args[]) {
   Child ch = new Child();
   Parent pt = ch;
   Grandparent gp = ch;
   Object ob = ch;
}

In this example there is one Child referent in memory associated with four different reference types. This method will compile, indicating all these types are legal references to a Child referent. It does not mean an Object reference can invoke a method that has been defined by a subclass. The Object reference only understands its own class interface. What the Object reference can access are the Object-specific attributes of the referent, which I will now start calling a concrete referent.

The same holds true for the Grandparent and Parent references. What they can see of the concrete referent is restricted to their own interfaces, including the ones they have inherited.

You can also construct a new concrete referent with a parent reference:

public static void main(String args[]) {
   Child ch = new Child();
   Parent pt = new Child();
   Grandparent gp = new Child();
   Object ob = new Child();
}

In this example I assigned a new Child referent to each reference type. Other than being different instances, these referents are all the same. What’s different is the access you have to each one, which is now limited by the reference type as well as accessibility modifiers.

Why would you want to do this? More to the point, why would the Java language promote this kind of flexibility? One answer is that subclasses let us specialize the state and behavior of an existing class, but we still need the ability to address many specific classes in one general way. In Java, you can do that by addressing an object by any type it has inherited.

With that in mind, you’re going to need two operations to help you keep your references and their referents straight. You need a way to test whether a referent conforms to a specific type. The instanceof operator provides that test. You also will need a way to assign a more specific reference to an existing referent. The casting operator provides that service.

To cast an object, you provide the name of a class in parentheses next to an expression that evaluates to an object referent, like this:

Child ch2 = (Child)pt;
Child ch3 = (Child)gp;
Child ch4 = (Child)ob;

Here I’ve cast each of the references from the previous code snippet to the Child class, then assigned the results to Child references. Casting tells the compiler to examine the reference and ensure that the operation results in a type that is legal for the assigned variable.


Review Chapter 3, “Using Java Operators and Conditional Logic,” to understand casting for primitive types.

Casting works in both directions, but it’s not required to cast “up.” Assigning an int value to a long variable requires no additional instruction because there is no possible loss of precision. In a similar sense, when assigning an Object reference to a Child referent, it’s always safe. Although an Object reference cannot access all the concrete referent’s members, they’re not going anywhere. They just require a more specific interface to get to them.

Casting “down,” on the other hand, tests whether the reference type can access every member available through the casting type. As the code snippet shows, this rule means casting statements often appear redundant, but the programmers must acknowledge to the compiler, when casting down, that it might not work.

Believe it or not, casting up can fail too. After all, you can’t just assign a String reference to a Child referent, as if Java supported magic type conversion:

// Won’t work!
String str = (String)ch1;

When you cast up in your code, the compiler completes the type check by examining the declared referent. When you cast down, there are certain run-time operations that can only be resolved as the code runs. Since the compiler can’t guarantee its type checking when you cast down, it requires you to spell out the test.

Sometimes you want to test a concrete referent for its actual type before you try casting to it. Older Java code in particular is full of examples like this:

Child ch5 = null;
if (pt instanceof Child)
ch5 = (Child)pt;

The instanceof operator takes an object reference and a class type as operands and performs a Boolean test. Code like this can protect a program from crashing. If you tried casting blind and the variable pt did not point to a Child referent, the JVM would have to throw an exception. A defensive test like this avoids that outcome.

But let’s say you have a Parent class that has been extended by several subclasses, such as Child1, Child2, and Child3. Also, let’s say the Parent class is used as a parameter in a method, like this:

public void checkStatus(Parent p) { … }

You could pass any subclass of Parent into this method with no worries. In other words, you can always cast up implicitly. The compiler can always confirm it. What if, however, you wanted to check the passed-in parameter and see if it is actually a specific subclass? You’d also use the instanceof operator to test that, as in this example:

public void checkStatus(Parent p) {
if (p instanceof Child1) return;
// otherwise do something with p
}

In Chapter 10, “Understanding Java Interfaces and Abstract Classes,” you’ll learn to avoid using the instanceof operator by using the enum type and switch statements.

If a type you use as a parameter inspires many subclasses, they will all be legal parameters for that method. If for some reason that method’s code has to sort through those subclasses and behave differently for some of them, you can end up writing a lot of defensive (testing) code before you get to do anything constructive. It can get out of hand pretty quickly if you’re not careful.

Implementing Polymorphic Behavior

certobjective.eps

Method names are important. The method names of one class, taken individually or as a whole, communicate an idea or intent to the calling programmer. The more methods a class has, the harder it becomes to interpret that intent. When designing a class with public methods, therefore, you should consider how many there are as well as what their names signify.


In my view, it’s just good defense to declare classes final. Allow for inheritance once the class fully supports it, but not a moment sooner.

These factors are even more important once you allow inheritance in a class. In my experience, you don’t let programmers inherit your classes; you dare them. Java programmers will start creating subclasses the minute they learn the extends keyword. It’s easy to use, and the compiler doesn’t monitor the quality of inheritance the programmer achieves.

Once you support it and have good method names to boot, you have great potential you can tap. To say more about that potential, I need to flip the thought process I used to introduce this chapter. To get you started, I used a Grandfather class that was extended by a Parent class that was extended by a Child class. This represents our sense in the natural world of how inheritance is supposed to work.

Organizing Objects by Type

This kind of ancestry is actually backward thinking in an object-oriented-type system. Think about it this way: In the natural world, is your child a kind of grandparent? Odds are it’s too early to tell. But every grandparent is, or was, a child. You could even say a grandparent is a child who grew up to have children who also grew up to have children. That’s an example of the perspective we need to develop a proper type system: A Grandparent class should specialize a Child class, not the other way around.

Here’s another example, this one in geometry. There are rectangles and squares. A rectangle is a shape whose opposing sides have the same length. So is a square, but it is also true that all its sides have the same length.

A less well-known case involves ellipses and circles. An ellipse (oval) is a set of all points in a plane that are equidistant from two other points. It’s like having two radii that, added together, represent one distance to a set of other points. A circle is the same, but for a circle, the two center points happen to be in the same location.

So a square is a special case of a rectangle, and a circle is a special case of an ellipse. All four of them are shapes, and all four of them have an area they contain. All four have different ways to calculate their area. With just that much information, you can at least imagine how an object-oriented drawing program might organize these ideas into related classes.

Using Method Names to Effect Polymorphism

Let’s say that all children play. So do their parents, and so do their grandparents—they just have different kinds of games, and perhaps they don’t play nearly as much. But let’s say children play the most kinds of games and have the most “general” play behavior. It would make sense, in terms of objects, to place a Child class at the top of a type system and add a play() method to it. The Parent class can then inherit the play() method and override the Child class’s logic. The Grandparent class can inherit from the Parent class, also making appropriate changes to its behavior.

To give the idea one more dimension, Figure 9-3 depicts this relationship using Father and Mother classes, which are extended by Grandfather and Grandmother classes, respectively. Each class has or inherits the play() method. I have repeated the method name in the figure to emphasize that each class will override it.

Figure 9-3: A type system of classes

c09f003.eps

Because these classes are related by inheritance, they share a type of behavior, not just the method name. Any other class can have a method by the same name. Classes that are related by type, however, enjoy a special facility called the polymorphic effect. Through polymorphism, you can invoke the play() method in a Grandfather object using a Child reference, and it will still behave like a Grandfather object.

No matter how you refer to a Grandfather referent—using a Father, Child, or even an Object reference—calling the play() method from it executes the behavior of that referent. So if you want all your Child objects to play at once, regardless of their concrete type, polymorphism guarantees an elegant way to express that intention.

Listing 9-8 shows an implementation of all five classes using various inheritance-based effects that you have studied so far, including member inheritance, overriding, and restricting member visibility to subclasses.

Listing 9-8: Polymorphic effect using the Child class and its subclasses

class Child {
private int fun = 10;
public void play() {
fun *= 2;
      System.out.println(report());
   }
private String report() {
return "I had fun!";
   }
}
class Father extends Child {
private short fun = 4;

   @Override public void play() {
fun += 2;
      log("play");
   }
private void log(String str) {
      System.out.println("Achieved fun level: " + fun);
   }
}
class Mother extends Child {
private short fun = 2;

   @Override public void play() {
fun++;
      remember();
   }
protected void remember() {
      System.out.println("Great time for all!");
   }
}
class Grandfather extends Father {
private byte fun = -10;

   @Override public void play() {
fun++;
      System.out.println(mutter());
   }
private String mutter() {
return "Oy it's hot.";
   }
}
class Grandmother extends Mother {
private byte fun = 1;

   @Override public void play() {
fun += 3;
      remember();
   }
}
class FunTest {
public static void main(String args[]) {
      Child[] kids = new Child[] {
new Child(), new Mother(),
new Father(), new Child(),
new Grandfather(), new Grandmother()
      };
for (Child kid : kids) {
         kid.play();
      }
   }
}
$ java FunTest
I had fun!
Great time for all!
Achieved fun level: 6
I had fun!
Oy it's hot.
Great time for all!

In the FunTest class, I declare and populate a simple array of Child references with various concrete types. In the for-each loop, I then call the play() method on each element. As the output shows, each concrete type reports the way it ought to.

The implementations supporting the play() method in each class are a little different. Each level of subclass, for example, uses a different primitive type to store the fun value. Also notice that the Grandmother class uses a remember() method it inherits from the Mother class. Each class encapsulates these support features as details of its implementation. Our test class only cares that it can invoke the play() method.

Naming behavior in a clear but common way gives polymorphic behavior its power. Once you have it, it becomes possible to invoke the same behavior on a variety of objects at one time and let each object execute as it should. To do this well, it’s essential to consider what type and behavior belong at the top of the scheme and which types represent specializations of that general behavior and design accordingly. It takes time to develop that skill, of course. Now, however, you have just enough information to track this kind of design through packages in the JDK and see why certain method names pop up again and again.


The Essentials and Beyond
In this chapter, we examined how inheritance works in Java and how it is different from creating a class by composition. You saw through the construction process how a Java object is made from a subclass. I described key expectations, such as the equality contract, that every class should either fulfill or document as unsupported if it allows inheritance.
Subclasses can specialize themselves by adding new methods and/or overriding inherited ones. They can also be referred to by variables of a parent type. I used the term concrete referent to distinguish between the object itself and the type of any legal reference for it. The casting and instanceof operators give us a way to test the concrete type of any referent and to get back to it. Finally, we took a look at what polymorphic behavior is, how to design for it, and how to implement it.
Additional Exercises
1. Write a test program to show that an array of type char inherits from the Object class.
2. Write a class called BadCast that casts a character array referent to a String reference. Also include a statement to construct a String object using the array. Compile and observe the results.
3. Modify the Options class in Listing 9-4 so that a null parameter, if passed into any valid constructor, is ignored and the member variable’s default value is preserved.
4. Write a class called OverTest that contains the methods priv(), stat(), and fin(). Declare these methods as private, static, and final, respectively. Next write a class called ChildTest that overrides each method. Compile the class and observe the error message for each case.
5. Write a Rectangle class that includes members for width and length. Include a method called area() that calculates and returns the area. Next, write a Square subclass; make sure the length and width parameters are the same; also, override the area() method with the specific equation for calculating a square’s area.
Review Questions
1. Which of the following statements identify a difference between inheritance and composition? (Choose all that apply.)

A. Only inherited members are type-safe.

B. Only subclasses can access protected members.

C. Only inherited methods can be overridden.

D. Only inherited methods can be overloaded.

2. Which of the following correctly describes inheritance?

A. You cannot overload a final method.

B. You cannot reuse the name of a parent class’s static method.

C. You cannot override a parent class’s private method.

D. You cannot declare a method final and static.

3. True or false: You can declare a main() method as final and it will compile.
4. Does the operation (obj instanceof Object) always return true? The member obj could be declared as any object type. Choose only the most specific correct option.

A. Yes, it always returns a true value.

B. No, it could return a false value.

C. It depends on the type of the obj member.

D. It depends on the value of the obj member.

5. What does the @Override annotation do for a method?

A. It tells the compiler to issue a warning if the method doesn’t perform an override.

B. It verifies that overriding is legal for the method.

C. It determines whether the method’s signature matches a method in the parent class.

D. It disables overloading for the method.

6. True or false: A class with only private constructors cannot have subclasses.
7. Assume you have a member variable obj that is not assigned the null value. Can you call obj.super.toString()?

A. Yes, it will work even if the obj reference has an Object referent.

B. No. The super keyword is not a class member.

C. It works if the toString() method called is an override of the Object class.

D. You cannot call it from a static method like main(), but a non-static method is okay.

8. How can you express a polymorphic effect in Java?

A. You need classes related by inheritance that use method overriding to create local effects.

B. You need classes with the same method names that are implemented the same way; you don’t need a shared type.

C. You need classes all in one package to inherit from the same class.

D. You only have to override methods. Polymorphism is in the eye of the beholder.

9. Assume you are preparing a system of types for a human resources application. Which class name would you use as the best parent to the other types?

A. Director

B. Executive

C. Employee

D. Manager

10. True or false: You can use the @Override annotation on a method that is declared final.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.224.226