Combining composition

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

and inheritance

//: PlaceSetting.java
// Combining composition & inheritance
 
class Plate {
  Plate(int i) {
    System.out.println("Plate constructor");
  }
}
 
class DinnerPlate extends Plate {
  DinnerPlate(int i) {
    super(i);
    System.out.println(
      "DinnerPlate constructor");
  }
}
 
class Utensil {
  Utensil(int i) {
    System.out.println("Utensil constructor");
  }
}
 
class Spoon extends Utensil {
  Spoon(int i) {
    super(i);
    System.out.println("Spoon constructor");
  }
}
 
class Fork extends Utensil {
  Fork(int i) {
    super(i);
    System.out.println("Fork constructor");
  }
}
 
class Knife extends Utensil {
  Knife(int i) {
    super(i);
    System.out.println("Knife constructor");
  }
}
 
// A cultural way of doing something:
class Custom {
  Custom(int i) {
    System.out.println("Custom constructor");
  }
}
 
public class PlaceSetting extends Custom {
  Spoon sp;
  Fork frk;
  Knife kn;
  DinnerPlate pl;
  PlaceSetting(int i) {
    super(i + 1);
    sp = new Spoon(i + 2);
    frk = new Fork(i + 3);
    kn = new Knife(i + 4);
    pl = new DinnerPlate(i + 5);
    System.out.println(
      "PlaceSetting constructor");
  }
  public static void main(String[] args) {
    PlaceSetting x = new PlaceSetting(9);
  }
} ///:~ 

While the compiler forces you to initialize the base classes, and requires that you do it right at the beginning of the constructor, it doesn’t watch over you to make sure that you initialize the member objects, so you must remember to pay attention to that.

Guaranteeing proper cleanup

//: CADSystem.java
// Ensuring proper cleanup
import java.util.*;
 
class Shape {
  Shape(int i) {
    System.out.println("Shape constructor");
  }
  void cleanup() {
    System.out.println("Shape cleanup");
  }
}
 
class Circle extends Shape {
  Circle(int i) {
    super(i);
    System.out.println("Drawing a Circle");
  }
  void cleanup() {
    System.out.println("Erasing a Circle");
    super.cleanup();
  }
}
 
class Triangle extends Shape {
  Triangle(int i) {
    super(i);
    System.out.println("Drawing a Triangle");
  }
  void cleanup() {
    System.out.println("Erasing a Triangle");
    super.cleanup();
  }
}
 
class Line extends Shape {
  private int start, end;
  Line(int start, int end) {
    super(start);
    this.start = start;
    this.end = end;
    System.out.println("Drawing a Line: " +
           start + ", " + end);
  }
  void cleanup() {
    System.out.println("Erasing a Line: " +
           start + ", " + end);
    super.cleanup();
  }
}
 
public class CADSystem extends Shape {
  private Circle c;
  private Triangle t;
  private Line[] lines = new Line[10];
  CADSystem(int i) {
    super(i + 1);
    for(int j = 0; j < 10; j++)
      lines[j] = new Line(j, j*j);
    c = new Circle(1);
    t = new Triangle(1);
    System.out.println("Combined constructor");
  }
  void cleanup() {
    System.out.println("CADSystem.cleanup()");
    t.cleanup();
    c.cleanup();
    for(int i = 0; i < lines.length; i++)
      lines[i].cleanup();
    super.cleanup();
  }
  public static void main(String[] args) {
    CADSystem x = new CADSystem(47);
    try {
      // Code and exception handling...
    } finally {
      x.cleanup();
    }
  }
} ///:~ 

Everything in this system is some kind of Shape (which is itself a kind of Object since it’s implicitly inherited from the root class). Each class redefines Shape’s cleanup( ) method in addition to calling the base-class version of that method using super. The specific Shape classes Circle, Triangle and Line all have constructors that “draw,” although any method called during the lifetime of the object could be responsible for doing something that needs cleanup. Each class has its own cleanup( ) method to restore non-memory things back to the way they were before the object existed.

Order of garbage collection

Name hiding

//: Hide.java
// Overloading a base-class method name
// in a derived class does not hide the
// base-class versions
 
class Homer {
  char doh(char c) {
    System.out.println("doh(char)");
    return 'd';
  }
  float doh(float f) {
    System.out.println("doh(float)");
    return 1.0f;
  }
}
 
class Milhouse {}
 
class Bart extends Homer {
  void doh(Milhouse m) {}
}
 
class Hide {
  public static void main(String[] args) {
    Bart b = new Bart();
    b.doh(1); // doh(float) used
    b.doh('x');
    b.doh(1.0f);
    b.doh(new Milhouse());
  }
} ///:~ 

As you’ll see in the next chapter, it’s far more common to override methods of the same name using exactly the same signature and return type as in the base class. It can be confusing otherwise (which is why C++ disallows it, to prevent you from making what is probably a mistake).



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

  • Organizations are increasingly gravitating toward mobile-first application development as they assess the need to revamp their application portfolios to support touch computing and mobility. Consumerization has brought higher expectations for application usability along with the mobile devices themselves. Enterprises are increasingly shifting their new application acquisitions and development efforts toward mobile platforms. With this backdrop, it is natural to expect application platform vendors to invest in …

  • By now you've likely heard of Agile development and building products in small incremental pieces, so you can get real feedback along the way. In fact, you may even be considering using Agile on your next project. But where do you start? Agile can take a lot of forms, such as Scrum or Kanban. Each form has advantages and disadvantages, but both will help your team get the right feedback they need to build great products. Read this white paper to find out which one is right for you.

Most Popular Programming Stories

More for Developers

RSS Feeds

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