Java ScreenShot
     

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 5.  Inheritance


Design Hints for Inheritance

We want to end this chapter with some hints for using inheritance that we have found useful.

  1. Place common operations and fields in the superclass.

    This is why we put the name field into the Person class, rather than replicating it in Employee and Student.

  2. Don't use protected fields.

    Some programmers think it is a good idea to define most instance fields as protected, "just in case," so that subclasses can access these fields if they need to. However, the protected mechanism doesn't give much protection, for two reasons. First, the set of subclasses is unbounded—anyone can form a subclass of your classes and then write code that directly accesses protected instance fields, thereby breaking encapsulation. And in the Java coding language, all classes in the same package have access to protected fields, whether or not they are subclasses.

    However, protected methods can be useful to indicate methods that are not ready for general use and should be redefined in subclasses. The clone method is a good example.

  3. Use inheritance to model the "is–a" relationship.

    Inheritance is a handy code-saver, and sometimes people overuse it. For example, suppose we need a Contractor class. Contractors have names and hire dates, but they do not have salaries. Instead, they are paid by the hour, and they do not stay around long enough to get a raise. There is the temptation to derive Contractor from Employee and add an hourlyWage field.

    class Contractor extends Employee
    { . . .
     private double hourlyWage;
    }
    


    This is not a good idea, however, because now each contractor object has both a salary and hourly wage field. It will cause you no end of grief when you implement methods for printing paychecks or tax forms. You will end up writing more code than you would have by not inheriting in the first place.

    The contractor/employee relationship fails the "is–a" test. A contractor is not a special case of an employee.

  4. Don't use inheritance unless all inherited methods make sense.

    Suppose we want to write a Holiday class. Surely every holiday is a day, and days can be expressed as instances of the GregorianCalendar class, so we can use inheritance.

    class Holiday extends GregorianCalendar { . . . }
    


    Unfortunately, the set of holidays is not closed under the inherited operations. One of the public methods of GregorianCalendar is add. And add can turn holidays into nonholidays:

    Holiday christmas;
    christmas.add(GregorianCalendar.DAY, 12);
    


    Therefore, inheritance is not appropriate in this example.

  5. Use polymorphism, not type information.

    Whenever you find code of the form

    if (x is of type 1)
     action1(x);
    else if (x is of type 2)
     action2(x);
    


    think polymorphism.

    Do action1 and action2 represent a common concept? If so, make the concept a method of a common superclass or interface of both types. Then, you can simply call

    x.action();
    


    and have the dynamic dispatch mechanism inherent in polymorphism launch the correct action.

    Code using polymorphic methods or interface implementations is much easier to maintain and extend than code that uses multiple type tests.

  6. Don't overuse reflection.

    The reflection mechanism lets you write programs with amazing generality, by detecting fields and methods at run time. This capability can be extremely useful for systems programming, but it is usually not appropriate in apps. Reflection is fragile—the compiler cannot help you find coding errors. Any errors are found at run time and result in exceptions.

Screenshot

Java ScreenShot
     
Top
 

Comments