Previous Table of Contents Next


14.3.8.2. Data Access Methods

In the Circle example we’ve been using, we’ve declared the circle position and radius to be public fields. In fact, the Circle class is one where it may well make sense to keep those visible—it is a simple enough class, with no dependencies between the variables.

On the other hand, suppose we wanted to impose a maximum radius on objects of the Circle class. Then it would be better to hide the r variable so that it could not be set directly. Instead of a visible r variable, we’d implement a setRadius() method that verifies that the specified radius isn’t too large and then sets the r variable internally. Example 14.20 shows how we might implement Circle with encapsulated data and a restriction on radius size. For convenience, we use protected fields for the radius and position variables. This means that subclasses of Circle, or cooperating classes within the shapes package are able to access these variables directly. To any other classes, however, the variables are hidden. Also, note the private constant and method used to check whether a specified radius is legal. And finally, notice the public methods that allow you to set and query the values of the instance variables.

EXAMPLE 14.20. Hiding variables in the Circle class.

   package shapes;
       // Specify a package for the class.

   public class Circle {
       // Note that the class is still public!
       protected double x, y;
       // Position is hidden, but visible to subclasses.
       protected double r;
       // Radius is hidden, but visible to subclasses.
       private static final double MAXR = 100.0;
       // Maximum radius (constant).
       private boolean check_radius(double r) { return (r    <= MAXR); }

       // Public constructors
       public Circle(double x, double y, double r) {
           this.x = x; this.y = y;
           if (check_radius(r)) this.r = r;
           else this.r = MAXR;
       }
       public Circle(double r) { this(0.0, 0.0, r); }
       public Circle() { this(0.0, 0.0, 1.0); }

       // Public data access methods
       public void moveto(double x, double y) { this.x = x; this.y = y;}
       public void move(double dx, double dy) { x += dx;  y += dy; }
       public void setRadius(double r)
           { this.r = (check_radius(r))?r:MAXR; }
       // Declare these trivial methods final so we don’t get dynamic
       // method lookup and so that they can be inlined by the compiler.
       public final double getX() { return x; };
       public final double getY() { return y; };
       public final double getRadius() { return r; };
   }

14.3.9. Abstract Classes and Interfaces

In Example 14.20, we declared our Circle class to be part of a package named shapes. Suppose we plan to implement a number of shape classes: Rectangle, Square, Ellipse, Triangle, and so on. We’ll give all of these shape classes our two basic area() and circumference() methods. Now, to make it easy to work with an array of shapes, it would be helpful if all our shape classes have a common superclass, Shape. We want Shape to encapsulate whatever features all our shapes have in common. In this case, what they have in common is the area() and circumference() methods. But our generic Shape class can’t actually implement these methods, since it doesn’t represent any actual shape. Java handles this case with abstract methods.

14.3.9.1. Abstract Methods

Java lets us define a method without implementing it by making the method abstract. An abstract method has no body; it simply has a signature definition followed by a semicolon.25 Here are the rules about abstract methods, and the abstract classes that contain them:


25An abstract method in Java is something like a “pure virtual function” in C++ (i.e., a virtual function that is declared = 0). In C++, a class that contains a pure virtual function is called an “abstract class” and may not be instantiated. The same is true of Java classes that contain abstract methods.
  Any class with an abstract method is automatically abstract itself, and must be declared as such.
  A class may be declared abstract even if it has no abstract methods. This prevents it from being instantiated.
  An abstract class cannot be instantiated.
  A subclass of an abstract class can be instantiated if it overrides each of the abstract methods of its superclass and provides an implementation (i.e., a method body) for all of them.
  If a subclass of an abstract class does not implement all of the abstract methods it inherits, that subclass is itself abstract.

That description of the abstract keyword was pretty abstract! Example 14.21 is more concrete. It shows an abstract Shape class and two non-abstract subclasses of it.

EXAMPLE 14.21. An Abstract class and subclasses.

   public abstract class Shape {
       public abstract double area();
       public abstract double circumference();
   }

   class Circle extends Shape {
       protected double r;
       protected static final double PI = 3.14159265358979323846;
       public Circle() { r = 1.0; }
       public Circle(double r) { this.r = r; }
       public double area() { return PI * r * r; }
       public double circumference() { return 2 * PI * r;    }
       public double getRadius() { return r; }
   }

   class Rectangle extends Shape {
       protected double w, h;
       public Rectangle() { w = 0.0; h = 0.0; }
       public Rectangle(double w, double h) { this.w = w;  this.h = h; }
       public double area() { return w * h; }
       public double circumference() { return 2 * (w + h);    }
       public double getWidth() { return w; }
       public double getHeight() { return h; }
   }


Previous Table of Contents Next