Inheritance

The top example seems entirely obvious. But the middle example documents what is happening implicitly. All classes in Java automatically inherit from the Java standard library class called Object. The syntax for designating inheritance uses the keyword extends. Class Object defines a method called toString(), and this method is called automatically whenever an object is passed to println(). If you want toString() to provide anything more interesting than class name and address, you have to "override" the definition inherited from the Object base class with your own.
   class InheritanceDemos {   // blissful ignorance
      private int attribute = 99;
   
      public static void main( String[] args ) {
         InheritanceDemos obj = new InheritanceDemos();
         System.out.println( "obj is " + obj );
   }  }
   
   // obj is InheritanceDemos@14e803fb
   
   
   class InheritanceDemos extends Object {   // what's happening behind
      private int attribute = 99;            //    the curtain
   
      public static void main( String[] args ) {
         InheritanceDemos obj = new InheritanceDemos();
         System.out.println( "obj is " + obj.toString() );
   }  }
   
   // obj is InheritanceDemos@14e803fb
   
   
   class InheritanceDemos extends Object {   // overriding the inherited
      private int attribute = 99;            //    toString() method
      public String toString() {
         return "InheritanceDemos.toString()---" + attribute;
      }
   
      public static void main( String[] args ) {
         InheritanceDemos obj = new InheritanceDemos();
         System.out.println( "obj is " + obj );
   }  }
   
   // obj is InheritanceDemos.toString()---99
The class Base has overloaded the member function foo() - there are two functions of the same name but different argument lists. The class Derived inherits from class Base. It has inherited the no-argument version of foo() and has overridden the 1-argument version.

Notice that even though Derived "is a" Base and therefore inherits the private attribute "one", the compiler will not allow Derived to access "one". Notice also that the Derived object is being used to initialize a Base object reference. This demonstrates a fundamental quality of the inheritance relationship. The class (or "type", or abstraction) of Base is like a large "set". The "set" known as Derived can then be viewed as a proper subset of the set Base. Base is the generalization, and Derived is the specialization. Every object of type Derived is simultaneously an object of type Base.

   class Base {                // overload and override
      private int one = 111;
      public void foo() {
         System.out.println( "Base.foo() - " + one );
      }
      public void foo( int in ) {
         System.out.println( "Base.foo(int) - " + in );
   }  }
   
   class Derived extends Base {
      private int two = 222;
      public void foo( int in ) {
         System.out.println( "Derived.foo(int) - " + in );
         // System.out.println( "Derived.foo(int) - " + one );  // ERROR - private
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base first  = new Base();
         Base second = new Derived();
         first.foo();
         first.foo( 123 );
         second.foo();
         second.foo( 234 );
   }  }
   
   // Base.foo() - 111
   // Base.foo(int) - 123
   // Base.foo() - 111
   // Derived.foo(int) - 234
Whenever a derived class is instantiated, its base class must be initialized by the compiler in some way. The default mechanism is to have the base class's default constructor invoked. This will happen right before the derived class's constructor is invoked.
   class Base {    // default initialization of the base class
      Base() {
         System.out.println( "Base default constructor:" );
   }  }
   
   class Derived extends Base {
      Derived( int in ) {
         System.out.println( "Derived 1-arg constructor - " + in );
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         System.out.println( "main" );
         new Derived( 123 );
   }  }
   
   // main
   // Base default constructor
   // Derived 1-arg constructor - 123
If there is no default constructor in the base class, then the compiler will generate an error when it tries to parse the derived class constructor.
   class Base {    // default initialization of the base class
      Base( int in ) {
         System.out.println( "Base 1-arg constructor - " + in );
   }  }
   
   class Derived extends Base {
      // ERROR: No constructor matching Base() found in class Base.
      Derived( int in ) {
         System.out.println( "Derived 1-arg constructor - " + in );
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         new Derived( 123 );
   }  }
If the programmer desires that a constructor other than the default constructor be invoked, then it is the responsibility of the programmer to communicate that desire to the compiler. The way this communication is accomplished is with super(). When it is used, super() must be the first executable line of code in the body of the derived class constructor. Below, one argument is being passed to super(), therefore the compiler knows to "map" to the 1-argument constructor in class Base.
   class Base {    // super - explicit initialization of the base class
      Base() {
         System.out.println( "Base default constructor:" );
      }
      Base( int in ) {
         System.out.println( "Base 1-arg constructor - " + in );
   }  }
   
   class Derived extends Base {
      Derived( int in ) {
         super( in );
         System.out.println( "Derived 1-arg constructor - " + in );
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         System.out.println( "main" );
         new Derived( 123 );
   }  }
   
   // main
   // Base 1-arg constructor - 123
   // Derived 1-arg constructor - 123
One of the pillars of object-oriented programming is "polymorphism". This is the strategy of promoting the common interface of a member function to the base class, and then burying the uncommon implementation details to individual base and derived classes. The "magic" that makes this possible is "dynamic binding". In C++, you have to explicitly ask for dynamic binding by using the keyword "virtual". In Java, all member functions are "virtual" by default.

Given the superset-subset relationship presented earlier, and the dynamic binding presented here, it is now time to demonstrate creating a "heterogeneous" collection of objects that can be treated "homogeneously". If we set up an array of base class object references, we can initialize that array with any combination of base and derived class objects; then, all other application code does not have to be tightly coupled to individual concrete classes, it can be loosely coupled to an abstract interface. Notice how the two for loops in main() don't have to do anything except tell each object in the array, "do whatever it is that you do best." If new derived classes are created, the for loops don't have to change at all.

Another point to be made here is: inherit, or override. You either do one, or the other, but not both. However, class Derived demonstrates a way you can effectively do both. The member function bar() has been overridden. That means the implementation in the base class has not been inherited. But, there is a way to "call back" to the implementation in the base class. The syntax requires the super keyword, and the "dot" operator.

   class Base {
      public void foo() { System.out.println( "Base.foo" ); }
      public void bar() { System.out.println( "Base.bar" ); }
   }
   class Subclass extends Base {
      public void foo() { System.out.println( "Subclass.foo" ); }
      public void bar() { System.out.println( "Subclass.bar" ); }
   }
   class Derived extends Base {
      public void foo() {
         System.out.println( "Derived.foo" );
      }
      public void bar() {
         System.out.print( "Derived.bar " );
         super.bar();
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base[] refs = {
                  new Subclass(), new Derived(), new Base(), new Subclass() };
         for (int i=0; i < refs.length; i++)
            refs[i].foo();
         System.out.println();
         for (int i=0; i < refs.length; i++)
            refs[i].bar();
   }  }
   
   // Subclass.foo
   // Derived.foo
   // Base.foo
   // Subclass.foo
   //
   // Subclass.bar
   // Derived.bar Base.bar
   // Base.bar
   // Subclass.bar
In the previous example, if there is no meaningful implementation of a base class's method that can be defined in the base class, then qualify the signature of the base class method with abstract. This tells the compiler that there is no definition associated with this declaration. Also, any class that has at least one abstract method, must be qualified with abstract as well. Any class that is abstract cannot be instantiated directly.
   abstract class Base {   // abstract method and abstract class
      abstract public void foo();
   }
   
   class Subclass extends Base {
      public void foo() { System.out.println( "Subclass.foo" ); }
   }
   class Derived extends Base {
      public void foo() { System.out.println( "Derived.foo" ); }
   }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         // Base obj = new Base();
         // ERROR: Base is an abstract class. It can't be instantiated
         Base[] refs = { new Subclass(), new Derived(), new Subclass() };
         for (int i=0; i < refs.length; i++)
            refs[i].foo();
   }  }
   
   // Subclass.foo
   // Derived.foo
   // Subclass.foo
   
   ////////////////////////////// lab - DilbertZone \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   ////////////////////////////// lab - VehicleLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   ////////////////////////////// lab - BigRedLabel \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
All private members of a class are inaccessible to clients of the class, and, to derived classes of the class. The access modifier protected allows for access by derived classes. Below, the protected data member in class Base is being accessed directly in class Derived, and the private data member is being accessed indirectly through the protected accessor function.
   class Base {     // protected member, accessor function
      private   int secret;
      protected int confidential;
      public Base( int in )     { secret = in; }
      protected int getSecret() { return secret; }
   }
   
   class Derived extends Base {
      public Derived( int one, int two ) {
         super( one );
         confidential = two;
      }
      public void foo() {
         System.out.println( "secret is " + getSecret()
                                + ", confidential is " + confidential );
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Derived ref = new Derived( 42, 24 );
         ref.foo();
   }  }
   
   // secret is 42, confidential is 24
Another way of achieving the same result, is to refactor the foo() method across both classes Base and Derived, and then use the "super dot" syntax to "call back" to the base class from the derived class.
   class Base {     // callback to base class, no accessor function
      private   int secret;
      protected int confidential;
      public Base( int in ) { secret = in; }
      protected void foo() { System.out.print( "secret is " + secret ); }
   }
   
   class Derived extends Base {
      public Derived( int one, int two ) {
         super( one );
         confidential = two;
      }
      public void foo() {
         super.foo();
         System.out.println( ", confidential is " + confidential );
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base ref = new Derived( 42, 24 );
         ref.foo();
   }  }
   
   // secret is 42, confidential is 24
   
   ////////////////////////////// lab - StackHanoi \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
The instanceof operator tests the class of an object at run-time. It returns true if the class of the left-hand operand is the same as, or is some subclass of, the class specified by the right-hand operand. If you want to know if an object reference is an instance of a single type only, the second loop below demonstrates how Java's reflection package can be used to discover that information.

You can also use the instanceof operator to test if a reference refers to an array. Since arrays are themselves objects in Java, this is natural enough, but the test that is performed actually checks two things: first it will check if the object is an array, then it checks if the element type of that array is some subclass of the element type of the right-hand operand.

   class Base { }     // instanceof operator, reflection [getClass(), getName()]
   class Derived extends Base { }
   class Deep extends Derived { }
   class Alone { }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
   
         Object[] objs = { new Base(), new Derived(), new Deep(), new Alone() };
         for (int i=0; i < objs.length; i++)
            if (objs[i] instanceof Base)
               System.out.println( objs[i].getClass().getName()
                  + " IS an instanceof" );
            else
               System.out.println( objs[i].getClass().getName()
                  + " is NOT an instanceof" );
   
         System.out.println();
         for (int i=0; i < objs.length; i++)
            if (objs[i].getClass() == Base.class)
               System.out.println( objs[i].getClass().getName() +" IS a Base");
            else
               System.out.println( objs[i].getClass().getName() +" is NOT a Base");
   }  }
   
   // Base IS an instanceof
   // Derived IS an instanceof
   // Deep IS an instanceof
   // Alone is NOT an instanceof
   // 
   // Base IS a Base
   // Derived is NOT a Base
   // Deep is NOT a Base
   // Alone is NOT a Base
finalize() methods in Java are like destructors in C++, except that they are only called if the garbage collector reclaims the memory associated with unused object references. You can't count on when, or if, the garbage collector runs; and as a result, you can't count on finalize() methods being called. In the past, System.runFinalizersOnExit( true ) has been available to cause all uncalled finalize() methods to be run before the Java run-time exits. Unfortunately, this method has been deprecated.

There have been lots of suggestions in articles for how to give "hints" to the garbage collector that it should do its thing, but these have proven to be weak. The commented code at the bottom of main() is one of those suggestions. The only sure way to get the job done is to do it yourself. If you have resources that are scarce and need to be returned in a timely and deterministic fashion, then define your own dispose() or release() method and document for your users that they are responsible for calling that method on object references that are no longer in use. "The more things change [Java relieving memory management headaches], the more they stay the same [Java mandating resource management headaches]."

"It is tempting to use Java's finalize() method for a destructor ... Objects are not guaranteed to be garbage-collected at all ... Any objects that are awaiting garbage collection when a program exits are not cleaned up ... Bottom line, if you must [return scarce resources held by objects], do it explicitly. A practical use for finalize() would be to verify that an object had been previously cleaned up [with a dispose() method] by inspecting a flag that the clean-up routine sets, and perhaps throwing an exception or otherwise logging an error if the flag was not set." [Chuck Allison, "Thinking in Objects", C/C++ Users Journal, July 99, p85]

   class Base {
      private static int count = 1;
      private int id;
      public Base() { id = count++; }
   
      protected void finalize() {
         System.out.println( "Base finalize - " + id );
   }  }
   
   class Derived extends Base {
      protected void finalize() {
         System.out.print( "Derived finalize   " );
         // if the base class finalize() method needs to be called, the
         // derived class must explicitly call it
         super.finalize();
   }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base[] refs = { new Derived(), new Base(), new Derived() };
         System.runFinalizersOnExit( true );
         // for (int i=0; i < refs.length; i++)
         //    refs[i] = null;
         // refs = null;
         // System.gc();
   }  }
   
   // Derived finalize   Base finalize - 3
   // Base finalize - 2
   // Derived finalize   Base finalize - 1
   
   //// Derived finalize   Base finalize - 1
   //// Base finalize - 2
   
   ////////////////////////////// lab - ShapesLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   ////////////////////////////// lab - DisplayLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Java supports single inheritance of implementation, and multiple inheritance of interface. The class/extends construct is "inheritance of implementation", and the interface/implements construct is "inheritance of interface". Single inheritance precludes some useful and correct designs. The problems of multiple inheritance arise from multiple inheritance of implementation. So Java provides a mechanism to inherit a contract without inheriting implementation. This is done by declaring an interface type instead of a class type.

An interface may only specify constants (always implicitly public static final) and methods (always implicitly public abstract). static methods are not allowed.

Interface definitions create type names just as class definitions do. Below, Base is a new type that can be used to achieve polymorphism across an array of concrete objects.

   interface Base { void foo(); }
   
   class Subclass implements Base {
      public void foo() { System.out.println( "Subclass.foo" ); }
   }
   class Derived implements Base {
      public void foo() { System.out.println( "Derived.foo" ); }
   }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base[] refs = { new Subclass(), new Derived(), new Subclass() };
         for (int i=0; i < refs.length; i++)
            refs[i].foo();
   }  }
   
   // Subclass.foo
   // Derived.foo
   // Subclass.foo
   
   ////////////////////////////// interface_factory.html \\\\\\\\\\\\\\\\\\\\\\\\\\
Below, Base was first designed as an interface with a single foo() method. Derived inherited from the interface and provided an implementation of its method. Then when bar() was added to Base, Derived no longer compiles. If instead, Base had originally been implemented as an abstract class, bar() could be added to Base (along with a default implementation), and Derived would continue to work with no changes necessary.
   // interface Base {
   //    void foo();
   //    void bar();
   // }
   
   abstract class Base {
      public abstract void foo();
      public void bar() { System.out.println( "Base.bar" ); }
   }
   
   // class Derived must be declared abstract. It does not define void bar()
   // from interface Base.
   // class Derived implements Base {
   class Derived extends Base {
      public void foo() { System.out.println( "Derived.foo" ); }
   }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Base ref = new Derived();
         ref.foo();
         ref.bar();
   }  }
   
   // Derived.foo
   // Base.bar
   
   ////////////////////////////// ObserverSensorSystem.java \\\\\\\\\\\\\\\\\\\\\\\
   ////////////////////////////// interface_articles.html \\\\\\\\\\\\\\\\\\\\\\\\\
A class can implements multiple interfaces, but can only extends a single class (even if that class has only abstract methods. An interface, unlike a class, can extends more than one interface.
   interface A { public void a(); }   // "multiple inheritance"
   interface B { public void b(); }
   interface C { public void c(); }
   interface D extends A, B, C { }
   
   class AB implements A, B {
      public void a() { System.out.println( "AB:a" ); }
      public void b() { System.out.println( "AB:b" ); }
   }
   class BC implements B, C {
      public void b() { System.out.println( "BC:b" ); }
      public void c() { System.out.println( "BC:c" ); }
   }
   class CA implements C, A {
      public void c() { System.out.println( "CA:c" ); }
      public void a() { System.out.println( "CA:a" ); }
   }
   class ABC extends CA implements D {
      public void b() { System.out.println( "ABC:b" ); }
   }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         A[] as = { new AB(), new CA(), new ABC() };
         B[] bs = { new AB(), new BC(), new ABC() };
         C[] cs = { new BC(), new CA(), new ABC() };
         for (int i=0; i < as.length; i++) as[i].a();
         for (int i=0; i < bs.length; i++) bs[i].b();
         for (int i=0; i < cs.length; i++) cs[i].c();
   }  }
   
   // AB:a
   // CA:a
   // CA:a
   // AB:b
   // BC:b
   // ABC:b
   // BC:c
   // CA:c
   // CA:c
The Enumeration interface is one of the most pervasive architectural elements in Java's standard library. A class that implements Enumeration has signed-up for a "contract" that requires it to be able to generate a series of Objects, one at a time. Each invocation of the nextElement() method returns successive elements of the series.

The functionality of the Enumeration interface has recently been duplicated by the Iterator interface. In addition, Iterator adds an optional remove() operation, and has shorter method names. New implementations should consider using Iterator in preference to Enumeration.

The example below demonstrates two classes that support the Enumeration interface: a custom class and a Java standard library class. Note that the custom class has no support for resetting itself so that subsequent traversals are possible, and, for multiple simultaneous traversals.

   import java.util.Enumeration;
   import java.util.Vector;
   
   class Derived implements Enumeration {      
      private String[] array = new String[10];
      private int      total = 0, current = 0;
   
      public void    add( String str ) { array[total++] = str; }
      public boolean hasMoreElements() { return current < total; }
      public Object  nextElement()     { return array[current++]; }
   }
   
   class InheritanceDemos {     
      public static void main( String[] args ) {
         Derived enumer = new Derived();
         Vector  vector = new Vector();
         for (int i=0; i < args.length; i++) {
            enumer.add( args[i] );
            vector.add( args[i] );
         }
         while (enumer.hasMoreElements())
            System.out.print( enumer.nextElement() + "   " );
         System.out.println();
         for (Enumeration e = vector.elements(); e.hasMoreElements(); )
            System.out.print( e.nextElement() + " - " );
   }  }
   
   // D:\Java> java InheritanceDemos one two three four five
   // one   two   three   four   five
   // one - two - three - four - five -
The class Hashtable stores "key/value" pairs, where the "key" can be any type of object; and, direct access to every pair is provided. The only way to traverse a Hashtable is with the Enumeration interface. Here is some sample code. The method keys() returns a list of the current keys, and the method elements() returns a list of the values.
   import java.util.Hashtable;
   import java.util.Enumeration;
   
   public class UtilDemos {
      public static void main( String[] args ) {
         Hashtable h = new Hashtable();
         h.put( "m1", new StringBuffer( "first" ) );
         h.put( "a2", new StringBuffer( "second" ) );
         h.put( "z3", new StringBuffer( "third" ) );
         h.put( "e4", new StringBuffer( "fourth" ) );
   
         System.out.print( "keys ------- " );
         for (Enumeration e = h.keys() ; e.hasMoreElements() ; )
            System.out.print( e.nextElement() + " -- " );
         System.out.print( "\nvalues ----- " );
         for (Enumeration e = h.elements() ; e.hasMoreElements() ; )
            System.out.print( e.nextElement() + " -- " );
   
         System.out.println( "\ncontains m1? " + h.containsKey( "m1" ) );
         System.out.println( "contains m2? " + h.containsKey( "m2" ) );
         ((StringBuffer) h.get( "z3" )).append( "-more" );
         System.out.println( "hashtable is " + h );
   }  }
   
   // keys ------- z3 -- m1 -- e4 -- a2 --
   // values ----- third -- first -- fourth -- second --
   // contains m1? true
   // contains m2? false
   // hashtable is {z3=third-more, m1=first, e4=fourth, a2=second}
Continuing the discussion of the Enumeration interface, here is another complete example. Unlike the Derived implements Enumeration example, this one does support resetting itself and multiple simultaneous traversals. The way these features are achieved is with the inner class Iterator. The BST.elements() method returns a base class Iterator object which implements a forward traversal. The BST.elements() method returns a derived class IteratorBackward object which implements a backward traversal.
   import java.util.Enumeration;  // Binary Search Tree and Enumeration interface
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         BST bst = new BST();
   
         System.out.print( "numbers:  " );
         for (int i=0, val; i < 10; i++) {
            val = (int) (Math.random() * 49 + 1);
            System.out.print( (val < 10 ? " " : "") + val + "  " );
            bst.add( val );
         }
         System.out.print( "\ntraverse: " );
         bst.traverse();
   
         System.out.print( "\nforward:  " );
         for (Enumeration e = bst.elements(); e.hasMoreElements() ; )
            System.out.print( e.nextElement() + "  " );
   
         System.out.print( "\nbackward: " );
         for (Enumeration e = bst.elements(-1); e.hasMoreElements() ; )
            System.out.print( e.nextElement() + "  " );
   
         System.out.print( "\n" );
         for (Enumeration f = bst.elements(), b = bst.elements(-1);
               f.hasMoreElements() ; )
            System.out.print( f.nextElement() + "-" + b.nextElement() + "  " );
   }  }
   
   // numbers:  22  49  10  20  44  28  41  18   5  19
   // traverse:  5  10  18  19  20  22  28  41  44  49
   // forward:   5  10  18  19  20  22  28  41  44  49
   // backward: 49  44  41  28  22  20  19  18  10   5
   //  5-49  10-44  18-41  19-28  20-22  22-20  28-19  41-18  44-10  49- 5
   
   class BST {
   
      public class Node {
         int value;  Node left;  Node right;
         public String toString() { return (value < 10 ? " " : "") + value; }
      }
   
      private class Iterator implements Enumeration {
         protected Node[] list;
         protected int    next;
   
         public Iterator() {
            list = new Node[size];
            next = 0;
            buildList( root );
            next = 0;
         }
         public boolean hasMoreElements() {
            if (size == 0) return false;
            return (next != size);
         }
         public Object nextElement() {
            return list[next++];
         }
         private void buildList( Node current ) {
            if (current.left != null) buildList( current.left );
            list[next++] = current;
            if (current.right != null) buildList( current.right );
      }  }
   
      public class IteratorBackward extends Iterator {
         public IteratorBackward() {
            next = list.length-1;
         }
         public boolean hasMoreElements() {
            if (size == 0) return false;
            return (next != -1);
         }
         public Object nextElement() {
            return list[next--];
      }  }
   
      private Node root;
      private int  size;
   
      public void add( int in ) {
         if (root == null) {
            root = new Node();
            root.value = in;
            size = 1;
            return; }
         add( in, root );
      }
      public void        traverse()            { traverse( root ); }
      public Enumeration elements()            { return new Iterator(); }
      public Enumeration elements( int dummy ) { return new IteratorBackward(); }
   
      private void add( int in, Node current ) {
         if (in < current.value)
            if (current.left == null) {
               current.left = new Node();
               current.left.value = in;
               size++;
            } else add( in, current.left );
         else
            if (current.right == null) {
               current.right = new Node();
               current.right.value = in;
               size++;
            } else add( in, current.right );
      }
      private void traverse( Node current ) {
         if (current.left != null) traverse( current.left );
         System.out.print( current + "  " );
         if (current.right != null) traverse( current.right );
   }  }

When to use interfaces

If multiple inheritance is important or even useful, interfaces should be used. However, an abstract class enables you to define and reuse some or all of the implementation easily, rather than by explicit delegation to a contained object. Delegation "is tedious to implement and error-prone, so using an abstract class should not be dismissed lightly." [Gosling, p101]

Any major class you expect to be extended should be implemented as both an interface, and a class that implements that interface. For example, suppose Java's Attributed feature had been provided as a class instead of its current interface Attributed and class AttributedImpl implements Attributed pair. Then programmers who wanted to create new classes that extended other existing classes could never use Attributed since you can extend only one class. Because Attributed is an interface, programmers have a choice: they can extends AttributedImpl directly and avoid the aggregation/delegation, or, they can implements Attributed and aggregate/delegate to a local instance of AttributedImpl.

Interface Segregation Principle: if a class has multiple distinct constituences, each of these should be provided its own "little interface" so that it can be decoupled from the others. A class should limit itself to single inheritance of implementation within its domain, and then allow itself to participate in multiple inheritance of interface across domains. Below, "Huston" is a domain, of which "Vince" is a specialization; and, "Race", "Creed", "Religion" are interfaces that stretch across domains (or family trees).

   public class InheritanceDemos {
   
      interface Race     { void iOriginate(); }
      interface Creed    { void iVote();      }
      interface Religion { void iBelieve();   }
   
      static abstract class Huston {
         public void iAm() { System.out.print( "  I am Huston." ); }
      }
      static class Vince extends Huston implements Race, Creed, Religion {
         public void iAm() {
            super.iAm();
            System.out.println( "  I am Vince." );
         }
         public void iOriginate() {
            System.out.println( "  I am a European-American." ); }
         public void iVote() {
            System.out.println( "  I am a conservative." ); }
         public void iBelieve() {
            System.out.println( "  I am a Babatist." ); }
      }
   
      static class Democrat implements Creed {
         public void iVote() { System.out.println( "  I am a liberal." ); }
      }
      static class Independent implements Creed {
         public void iVote() { System.out.println( "  I am a libertarian." ); }
      }
   
      static void stereotypeMeOnTheBasisOfRace( Race stranger ) {
         stranger.iOriginate(); }
      static void stereotypeMeOnTheBasisOfCreed( Creed stranger ) {
         stranger.iVote(); }
      static void stereotypeMeOnTheBasisOfReligion( Religion stranger ) {
         stranger.iBelieve(); }
   
      public static void main( String[] args ) {
         Vince    me     = new Vince();
         Creed[]  people = { me, new Democrat(), new Independent() };
   
         System.out.println( "I support many types or roles or interfaces:" );
         me.iAm();
         stereotypeMeOnTheBasisOfRace( me );
         stereotypeMeOnTheBasisOfReligion( me );
   
         System.out.println( "We are plug-compatible:" );
         for (int i=0; i < 3; i++)
            stereotypeMeOnTheBasisOfCreed( people[i] );
   }  }
   
   // I support many types or roles or interfaces:
   //   I am Huston.  I am Vince.
   //   I am a European-American.
   //   I am a Babatist.
   // We are plug-compatible:
   //   I am a conservative.
   //   I am a liberal.
   //   I am a libertarian.
   
   ////////////////////////////// lab - DrawLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
In the domain below, directories contain files, each of which could be a directory. With the current design, the Directory implementation of ls() requires type checking (the instanceof operator) and type casting ((File) obj and (Directory) obj). Object-oriented design routinely discourages this kind of design because it is hard to maintain as the number and type of File-like classes changes over time. The addition of one small interface would quickly improve this example.
   import java.util.*;   // Composite design pattern
                         //    recursive hasa up the isa hierarchy
   class File {
      private String name;
      public File( String n ) { name = n; }
      public void ls()        { System.out.print( name + ". " ); }
   }
   class Directory {
      private String name;
      private Vector children = new Vector();
      public Directory( String n )  { name = n; }
      public void add( Object ele ) { children.addElement( ele ); }
      public void ls() {
         System.out.print( name + "/ " );
         for (Enumeration e = children.elements() ; e.hasMoreElements() ; ) {
            Object obj = e.nextElement();
            if (obj instanceof File) ((File) obj).ls();
            else                     ((Directory) obj).ls();
   }  }  }
   
   public class InheritanceDemos {
      public static void main( String[] args ) {
         Directory top = new Directory( "usr" );
         top.add( new File( "1_fstab" ) );
   
         Directory second = new Directory( "local" );
         second.add( new File( "2_profile" ) );
         second.add( new File( "2_cshrc" ) );
         top.add( second );
   
         Directory third = new Directory( "bin" );
         third.add( new File( "3_phone" ) );
         top.add( third );
   
         top.add( new File( "1_words" ) );
         top.ls();
         System.out.println();
   }  }
   
   // usr/ 1_fstab. local/ 2_profile. 2_cshrc. bin/ 3_phone. 1_words.
interface AbsFile has been added. Both File and Directory declare an inheritance relationship with this new "lowest common denominator" abstraction. The signature of all methods that are common to File and Directory have been added to AbsFile. Now, the Directory implementation of ls() doesn't have to check and cast for type, it simply relies on polymorphism.
   interface AbsFile { public void ls(); }
      
   class File implements AbsFile { ... }
   
   class Directory implements AbsFile {
      // ...
      public void add( AbsFile ele ) { ... }
      public void ls() {
         System.out.print( name + "/ " );
         for (Enumeration e = children.elements() ; e.hasMoreElements() ; )
            ((AbsFile)e.nextElement()).ls();
   }  }
   
   ////////////////////////////// lab - CompositeLab \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\