Designing with inheritance

Bruce Eckel's Thinking in Java Contents | Prev | Next

//: Transmogrify.java
// Dynamically changing the behavior of
// an object via composition.
 
interface Actor {
  void act();
}
 
class HappyActor implements Actor {
  public void act() { 
    System.out.println("HappyActor"); 
  }
}
 
class SadActor implements Actor {
  public void act() { 
    System.out.println("SadActor");
  }
}
 
class Stage {
  Actor a = new HappyActor();
  void change() { a = new SadActor(); }
  void go() { a.act(); }
}
 
public class Transmogrify {
  public static void main(String[] args) {
    Stage s = new Stage();
    s.go(); // Prints "HappyActor"
    s.change();
    s.go(); // Prints "SadActor"
  }
} ///:~ 

A Stage object contains a handle to an Actor, which is initialized to a HappyActor object. This means go( ) produces a particular behavior. But since a handle can be re-bound to a different object at run time, a handle for a SadActor object can be substituted in a and then the behavior produced by go( ) changes. Thus you gain dynamic flexibility at run time. In contrast, you can’t decide to inherit differently at run time; that must be completely determined at compile time.

Pure inheritance vs. extension

Downcasting and run-time

type identification

//: RTTI.java
// Downcasting & Run-Time Type
// Identification (RTTI)
import java.util.*;
 
class Useful {
  public void f() {}
  public void g() {}
}
 
class MoreUseful extends Useful {
  public void f() {}
  public void g() {}
  public void u() {}
  public void v() {}
  public void w() {}
}
 
public class RTTI {
  public static void main(String[] args) {
    Useful[] x = {
      new Useful(),
      new MoreUseful()
    };
    x[0].f();
    x[1].g();
    // Compile-time: method not found in Useful:
    //! x[1].u();
    ((MoreUseful)x[1]).u(); // Downcast/RTTI
    ((MoreUseful)x[0]).u(); // Exception thrown
  }
} ///:~ 

As in the diagram, MoreUseful extends the interface of Useful. But since it’s inherited, it can also be upcast to a Useful. You can see this happening in the initialization of the array x in main( ). Since both objects in the array are of class Useful, you can send the f( ) and g( ) methods to both, and if you try to call u( ) (which exists only in MoreUseful) you’ll get a compile-time error message.

If you want to access the extended interface of a MoreUseful object, you can try to downcast. If it’s the correct type, it will be successful. Otherwise, you’ll get a ClassCastException. You don’t need to write any special code for this exception, since it indicates a programmer error that could happen anywhere in a program.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: February 12, 2015 The evolution of systems engineering with the SysML modeling language has resulted in improved requirements specification, better architectural definition, and better hand-off to downstream engineering. Agile methods have proven successful in the software domain, but how can these methods be applied to systems engineering? Check out this webcast and join Bruce Powel Douglass, author of Real-Time Agility, as he discusses how agile methods have had a tremendous …

  • The mobile market is white hot. Building a well-crafted product development plan that addresses market research, strategy, design, and development will provide the greatest chance for success. Each phase of an app's lifecycle is critical to the its overall success and feeds into the next step of the process of product development for the app. This white paper examines the five key phases of mobile app creation, one by one, to understand how they work together to help create a successful mobile app.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date