Spring Semester 2003


Review and Tutorial: Inheritance and Abstract Classes

Review and Tutorial: Extending Classes

(In which Dilbert makes a return. As can be seen on the right these notes are picking some momentum. You should read what follows even if you know it all, just to verify your understanding is for real.

But you can also skip this section the first time through it).

Let's make a quick summary first (please refer to the first Dilbert Review).

Here are the things that should be already known:

Things that will be covered in this Second Review and Tutorial: Classes and Objects

A class is a collection of data and methods that operate on that data. The data and methods, taken together, usually serve to define the contents and capabilities of some kind of object. For example, a circle can be described by the x, y position of its center and by its radius (r). There are a number of things we can do with circles: compute their circumference, compute their area, check whether points are inside them, and so on. Each circle is different (i.e., has a different center or radius), but as a class, circles have certain intrinsic properties that we can capture in a definition:

public class Circle {
  public double x, y; // The coordinates of the center 
  public double r;    // The radius 

  // Methods that return the circumference and area of the circle 
  public double circumference () {
    return 2 * 3.141592 * r; 
  } 
  public double area () {
    return 3.141592 * r * r; 
  } 
}
Object Creation

Here's how we create a circle object:

Circle c = new Circle();
The way it works is this: The new keyword creates a new dynamic instance of the class -- i.e., it allocates the new object. The constructor method is then called, passing the new object implicitly (a this reference) and passing the arguments specified between parentheses (if any) explicitly.

The constructor can then start working on the object for initialization purposes.

Defining a Constructor

There is some obvious initialization we could do for our circle objects, so let's define a constructor. The code below shows a constructor that lets us specify the initial values for the center and radius of our new Circle object:

public class Circle {
  public double x, y, r; // The center and the radius of the circle 

  // The constructor method (or minitialization procedure) 
  public Circle(double x, double y, double r) {
    this.x = x; 
    this.y = y;
    this.r = r; 
  }
  
  public double circumference () {
    return 2 * 3.141592 * r; 
  } 
  public double area () {
    return 3.141592 * r * r; 
  } 

}
The part that we have not seen before (that is, the constructor) is in blue.

There are two important notes about naming and declaring constructors:

Multiple Constructors

Sometimes you'll want to be able to initialize an object in a number of different ways, depending on what is most convenient in a particular circumstance. For example, we might want to be able to initialize the radius of a circle without initializing the center, or we might want to initialize a circle to have the same center and radius as another circle, or we might want to initialize all the fields to default values.

Doing this is no problem: A class can have any number of constructor methods.

The example below shows how:

public Class Circle {
  public double x, y, r; 
  
  public Circle(double x, double y, double r) {
    this.x = x; this.y = y; this.r = r; 
  } 

  public Circle(double r) { x = 0.0; y = 0.0; this.r = r;   } 
  public Circle(Circle c) { x = c.x; y = c.y;      r = c.r; } 
  public Circle()         { x = 0.0; y = 0.0;      r = 1.0; } 
 
  public double circumference () { return 2 * 3.141592 * r; } 
  public double area () { return 3.141592 * r * r; }   

}
Again, the new code is in blue.

Method Overloading

The surprising thing in this example could be that all the constructor methods have the same name! So how can the compiler tell them apart? The way that you and I tell them apart is that the four methods take different arguments and are useful in different circumstances. The compiler tells them apart in the same way. In Java a method is distinguished by its name, and by the number, type, and position of its arguments. This is not limited to constructor methods -- any two methods are not the same unless they have the same name, and the same number of arguments of the same type passed at the same position in the argument list. When you call a method and there's more than one method with the same name, the compiler automatically picks the one that matches the data types of the arguments you are passing.

Defining methods with the same name and different argument types is called method overloading. It can be a convenient technique, as long as you only give methods the same name when they perform similar functions on slightly different forms of input data. Overloaded methods may have different return types, but only if they have different arguments. Don't confuse method overloading with method overriding, which we'll discuss later.

this Again

There's a specialized use of the this keyword that arises when a class has multiple constructors -- it can be used from a constructor to invoke one of the other constructors of the same class. So we could rewrite the additional constructors from the previous example in terms of the first one like this:

public Circle(double x, double y, double r) {
  this.x = x; this.y = y; this.r = r; 
}
public Circle(double r) { this(0.0, 0.0,   r); }
public Circle(Circle c) { this(c.x, c.y, c.r); }
public Circle()         { this(0.0, 0.0, 1.0); }
Here the this() call refers to whatever constructor of the class takes the specified type of arguments. This would be a more impressive example, of course, if the first constructor that we were invoking did a more significant amount of initialization, as it might, for example, if we were writing a more complicated class.

There is a very important restriction on this this syntax that is, as an invocation: it may only appear as the first statement in a constructor. It may, of course, be followed by any additional initialization that a particular version of the constructor needs to do. The reason for this restriction involves the automatic invocation of superclass constructor methods, to which we turn now.

Subclasses and Inheritance

This Circle class is good for abstract mathematical manipulation. For some applications this is exactly what we need. For other applications, we might want to be able to manipulate circles and draw them on the screen. This means we need a new class, GraphicCircle, that has all the functionality of Circle, but also has the ability to be drawn.

We want to implement GraphicCircle so that it can make use of the code we've already written for Circle. One way to do that is the following:

public class GraphicCircle {
  public double x, y; 
  public double r;  
  public Color outline, fill; 

  public double circumference () { 
    return 2 * 3.141592 * r; 
  } 
  public double area () {
    return 3.141592 * r * r; 
  }  
  public void draw(DrawWindow dw) {
     /* code omitted */ 
  } 
}
The part that we have taken from the previous class is in blue.

This approach would work but it is not particularly elegant. The problem is that we have to literally carry the code with us, and rewrite it. It would be nice if we didn't have to do that.

Extending a Class

Well, we really don't have to do it that way. We can define GraphicCircle as an extension, or subclass of class Circle.

public class GraphicCircle extends Circle { 
  /* We automatically inherit the variables and methods of
     class Circle, so we only have to put the new stuff here.
     We omit the constructor for GraphicCircle, for now. */
  Color outline, fill; 
  public void draw(DrawWindow dw) {
    dw.drawCircle(x, y, r, outline, fill); 
  } 
}
The part that we take from Circle is also represented in blue, the mechanism is provided by the object-oriented nature of Java, and we have come up with something reasonable as the code for the instance method of GraphicCircle, that is draw().

The extends keyword tells Java that GraphicCircle is a subclass of Circle, and that it inherits the fields and methods of that class (except for private fields and methods). The definition of the draw() method shows variable inheritance - this method uses the Circle variables x, y, and r as if they were defined right in GraphicCircle itself.

GraphicCircle also inherits the methods of Circle. Thus, if we have a GraphicCircle object referred to by variable gc, we can say:

double area = gc.area(); 
This works just as if the area() method were defined in GraphicCircle itself. So this is a convenient way of reusing the Circle class's code.

Another feature of subclassing is that every GraphicCircle object is also a perfectly legal Circle object. Thus, if gc refers to a GraphicCircle object, we can assign it to a Circle variable, and we can forget all about its extra graphic capabilities:

Circle c = gc;
That is, it's up to us if we want to ignore its graphic capabilities added by GraphicCircle.

Final Classes

When a class is declared with the final modifier, it means that it cannot be extended or subclassed. java.lang.System is an example of a final class. Declaring a class final prevents unwanted extensions to the class. (But it also allows the compiler to make some optimizations when invoking the methods of the class.)

Superclasses, Object, and the Class Hierarcy

In our example GraphicCircle is a subclass of Circle. We can also say that Circle is the superclass of GraphicCircle. The superclass of a class is specified in its extends clause:

public class GraphicCircle extends Circle { ... }
Every class you define has a superclass. If you do not specify the superclass with an extends clause, the superclass is the class Object. Object is a special class for a couple of reasons: You can look at class Object here.

http://java.sun.com/products/jdk/1.4/docs/api/java/lang/Object.html
Because every class has a superclass, classes in Java form a class hierarchy, which can be represented as a tree with Object at its root. The diagram below shows a class hierarchy which includes our Circle and GraphicCircle classes, as well as some of the standard classes from the Java API.

Object -+- Circle --- GraphicCircle
        |
        +- Math 
        | 
        +- System
        | 
        +- Component -+- Container --- Panel --- Applet 
                      | 
                      +- Button 
                      | 
                      +- List 
The complete class hierarchy for the Java 1.1 API is diagrammed here.

http://java.sun.com:80/products/jdk/1.1/docs/api/tree.html
Here's the latest.

http://java.sun.com/products/jdk/1.4/docs/api/overview-tree.html
Subclass Constructors

In our first example of GraphicCircle we left out the constructor method for our new GraphicCircle class. Let's implement it now. Here's one way:

public GraphicCircle (double x, double y, 
                      double r, 
                      Color outline, 
                      Color fill) {
  this.x = x; 
  this.y = y; 
  this.r = r;
  this.outline = outline; 
  this.fill = fill; 
}
The constructor relies on the fact that GraphicCircle inherits all of the variables of Circle and simply initializes those variables itself. But this duplicates the code of the Circle constructor, and if Circle did more elaborate initialization, it could become quite wasteful. (Same problem with when we tried to define a GraphicCircle for the first time). Furthermore, if the Circle class had internal private fields (discussed later) we wouldn't be able to initialize them like this. What we need is a way of calling a Circle constructor from within our GraphicCircle constructor, and that is provided with the extends mechanism, by Java.

Here's how we invoke a superclass's constructor:

public GraphicCircle (double x, double y, 
                      double r, 
                      Color outline, 
                      Color fill) {
  super(x, y, r); 
  this.outline = outline;
  this.fill = fill; 
}
super is a reserved word in Java. One of its uses is that shown in the example -- to invoke the constructor method of a superclass. Its use is analogous to the use of the this keyword to invoke one constructor method of a class from within another. Using super to invoke a constructor is subject to the same restrictions as using this to invoke a constructor:

Constructor Chaining

When you define a class, Java guarantees that the class's constructor method is called whenever an instance of that class is created. It also guarantees that the constructor is called when an instance of any subclass is created. In order to guarantee this second point, Java must ensure that every constructor method calls its superclass constructor method. If the first statement in a constructor is not an explicit call to a constructor of the superclass with the super keyword, then Java implicitly inserts the call super() -- that is, it calls the superclass constructor with no arguments. If the superclass does not have a constructor that takes no arguments, this causes a compilation error.

There is one exception to the rule that Java invokes super() implicitly if you do not do so explicitly. If the first line of a constructor, C1, uses the this() syntax to invoke another constructor, C2, of the class, Java relies on C2 to invoke the superclass constructor, and does not insert a call to super() into C1. Of course, if C2 itself uses this() to invoke a third constructor, C2 does not call super() either, but somewhere along the chain, a constructor either explicitly or implicitly invokes the superclass constructor, which is what is required.

Consider what happens when we create a new instance of the GraphicCircle class. First, the GraphicCircle constructor shown in our previous example is invoked. This constructor explicitly invokes a Circle constructor and that Circle constructor implicitly calls super() to invoke the constructor of its superclass, Object. The body of the Object constructor runs first, followed by the body of the Circle constructor, and finally followed by the body of the GraphicCircle constructor.

What this all means is that constructor calls are "chained" -- any time an object is created, a sequence of constructor methods are invoked, from subclass to superclass on up to Object at the root of the class hierarchy. Because a superclass constructor is always invoked as the first statement of its subclass constructor, the body of the Object constructor always runs first, followed by the body of its subclass, and on down the class hierarchy to the class that is being instantiated.

The Default Constructor

There is one missing piece in the description of constructor chaining above. If a constructor does not invoke a superclass constructor, Java does so implicitly. But what if a class is declared without any constructor at all? In this case, Java implicitly adds a constructor to the class. This default constructor does nothing but invoke the superclass constructor.

For example, if we did not declare a constructor for the GraphicCircle class, Java would have implicitly inserted this constructor:

public GraphicCircle() { super(); }
Note that if the superclass, Circle() did not declare a no-argument constructor, then this automatically inserted default constructor would cause a compilation error. If a class does not define a no-argument constructor, then all of its subclasses must define constructors that explicitly invoke the superclass constructor with the necessary arguments.

It can be confusing when Java implicitly calls a constructor or inserts a constructor definition into a class -- something is happening that does not appear in your code! Therefore, it is good coding style, whenever you rely on an implicit superclass constructor call or on a default constructor, to insert a comment noting this fact. Your comments might look like those in the following example:

class A {
  int i; 
  public A() {
    // Implicit call to super() here 
    i = 3; 
  }
}

class B extends A {
  // Default constructor: public B() { super(); }
}
If a class does not declare any constructor, it is given a public constructor by default. Classes that do not want to be publically instantiated, should declare a protected constructor to prevent the insertion of this public constructor. Classes that never want to be instantiated at all (in one particular, specific way,) should define that particular constructor private.

Shadowed Variables

Suppose that our GraphicCircle class has a new variable that specifies the resolution, in dots per inch, of the DrawWindow object in which it is going to be drawn. And further, suppose that it names that new variable r:

public class GraphicCircle extends Circle {
  Color outline, fill; 
  float r; // New variable. Resolution in dots-per-inch.
  public GraphicCircle(double x, double y, 
                       double rad, 
                       Color o, Color f) {
    super(x, y, rad); outline = o; fill = f; 
  } 
  public void setResolution(float resolution) { 
    r = resolution; 
  } 
  public void draw(DrawWindow dw) { 
    dw.drawCircle(x, y, r, outline, fill); 
  } 
}
Now, with this resolution variable declared, when we use the variable r in the GraphicCircle class, we are no longer referring to the radius of the circle. The resolution variable r in GraphicCircle shadows the radius variable r in Circle. (This is a contrived example, of course -- we could simply rename the variable and avoid the issue. Typically we would rename the variable: variable shadowing is a necessary part of Java syntax, but is not a useful programming technique. Your code will be easier to understand if you avoid shadowed variables).

So, how can we refer to the radius variable defined in the Circle class when we need it? Recall that using a variable, such as r, in the class in which it is defined is shorthand for:

this.r // Refers to the GraphicCircle resolution variable.
As you might guess, you can refer to a variable r defined in the superclass like this:
super.r // Refers to the Circle radius variable.
Another way you can do this is to cast this to the appropriate class and then access the variable:
((Circle)this).r
This cast is exactly what the super does when used like this. You can use this casting technique when you need to refer to a shadowed variable defined in a class that is not the immediate superclass. For example, if C is a subclass of B, which is a subclass of A, and class C shadows a variable x that is also defined in classes A and B,
A(x) -+
      |
      +- B(x) -+
               |
               +- C(x) 
then you can refer to these different variables from class C as follows:
x              // Variable x in class C
this.x         // Variable x in class C
super.x        // Variable x in class B
((B)this).x    // Variable x in class B
((A)this).x    // Variable x in class A 
But note this:
super.super.x  // Illegal; does not refer to x in class A
You cannot refer to a shadowed variable x in the superclass of a superclass with super.super.x. Java does not recognize this syntax.

Shadowed Methods?

Just as a variable defined in one class can shadow a variable with the same name in a superclass, you might expect that a method in one class could shadow a method with the same name (and same arguments) in a superclass. In a sense, they do: "shadowed" methods are called overridden methods. But method overriding is significantly different than variable shadowing; it is discussed below.

Overriding Methods

When a class defines a method using the same name, return type, and arguments as a method in its superclass, the method in the class overrides the method in the superclass. When the method is invoked for an object of the class, it is the new definition of the method that is called, not the superclass's old definition.

Method overriding is an important and useful technique in object-oriented programming. Suppose we define a class Ellipse of our Circle class. (This is admittedly a strange thing to do, since, mathematically, a circle is a kind of ellipse, and it is customary to derive a more specific class from a more general one. Nevertheless it is a useful example here). Then it would be important for Ellipse to override the area() and circumference() methods of Circle. Ellipse would have to implement new versions of these functions because the formulas that apply to circles don't work for ellipses.

Before we go any further with the discussion of method overridding, be sure that you understand the difference between method overriding and method overloading, which we discussed earlier. As you probably recall, method overloading refers to the practice of defining multiple methods (in the same class) with the same name but with differing argument lists. This is very different from method overriding, and it is important not to get them confused!

Overriding Is Not Shadowing

Although Java treats the variables and methods of a class analogously in many ways, method overriding is not like variable shadowing at all: You can refer to shadowed variables simply by casting an object to the appropriate type. You cannot invoke overridden methods with this technique, however.

The next example illustrates this crucial difference:

class A {
  int i = 1; 
  int f() { return i; } 
}

class B extends A {
  int i = 2;                   // Shadows variable i in class A.
  int f() { return -i; }       // Overrides method f in class A.
}

public class override_test {
  public static void main(String[] args) {
    B b = new B();          
    System.out.println(b.i);   // Refers to B.i; prints 2. 
    System.out.println(b.f()); // Refers to B.f(); prints -2. 

    A a = (A) b;               // Cast b to an instance of class A. 
    System.out.println(a.i);   // Now refers to A.i; prints 1. 
    System.out.println(a.f()); // Still refers to b.f(); prints -2.

  } 
}
While this difference between method overriding and variable shadowing may seem surprising at first, a little thought makes the purpose clear. Suppose we have a bunch of Circle and Ellipse (a subclass of Circle here) objects that we are manipulating. To keep track of the circles and ellipses, we store them in an array of type Circle[], casting all the Ellipse objects to Circle objects before we store them. Then, when we loop through the elements of this array, we don't have to know or care whether the element is actually a Circle or an Ellipse. What we do care very much about, however, is that the correct value is computed when we invoke the area() method of any element of the array. That is, we don't want to use the formula for the area of a circle when the object is actually an ellipse.

Seen in this context, it is not surprising at all that method overriding is handled differently by Java than variable shadowing.

final Methods

If a method is declared final, it means that the method declaration is the "final" one -- that it cannot be overridden. static methods and private methods cannot be overridden either, nor can the methods of a final class. (If a method cannot be overridden, the compiler may perform certain optimizations on it, too).

Dynamic Method Lookup

If we have an array of Circle and Ellipse Objects (or, say, Shapes,) how does the compiler know to call the Circle area() method or the Ellipse area() method for any given item in the array? The compiler does not know this; it can't. The compiler knows that it does not know, however, and produces code that uses "dynamic method lookup" at run-time. When the interpreter runs the code, it looks up the appropriate area() method to call for each of the objects. That is, when the interpreter interprets the expression s.area(), it dynamically looks for an area() method associated with the particular object referred to by the variable s. It does not simply use the area() method that is statically associated with the type of the variable s.

So the actual type of the object is used not the type of the object reference.

class Point {
  int x, y; 
  void clear() { 
    // code for clearing a Point object 
  } 
}

// Pixel is a Point with a Color 
class Pixel extends Point {
  Color color;
  void clear() {
    // code for clearing a Pixel object 
  } 
} 

// ... somewhere in a method we have: 
Point point = new Pixel(); 
point.clear(); // uses Pixel's clear()
Dynamic method lookup is fast, but it is not as fast as invoking a method directly. Fortunately, there are a number of cases in which Java does not need to use dynamic method lookup. static methods cannot be overridden, so they are always invoked directly. private methods are not inherited by subclasses and so cannot be overridden by subclasses; this means the Java compiler can safely invoke them without dynamic method lookup as well. final methods are invoked directly for the same reason: they cannot be overridden. Finally, when a method of a final class is invoked through an instance of the class or a subclass of it, then it, too, can be invoked without the overhead of dynamic lookup.

Invoking an Overridden Method

We've seen the important differences between method overriding and variable shadowing. Nevertheless, the Java syntax for invoking an overridden method is very similar to the syntax for accessing a shadowed variable: both use the super keyword. The following example illustrates this:

class A {
  int i = 1; 
  int f() { return i; }    // A very simple method. 
}

class B extends A {
  int i;                   // This variable shadows i in A. 
  int f() {                // This method overrides f() in A.
    i = super.i + 1;       // It retrieves A.i this way. 
    return super.f() + i;  // And it invokes A.f() this way. 
  } 
}
Recall that when you use super to refer to a shadowed variable, it is the same as casting this to the superclass type and accessing the variable through that. On the other hand, using super to invoke an overridden method is not the same as casting this. In this case, super has the special purpose of turning off dynamic method lookup and invoking the specific method that the superclass defines or inherits. (One needs to be aware of the limitations of the analogy with casting this in the case of referencing methods with super). So, please, make a note of it.

In the example above we use super to invoke an overridden method that is actually defined in the immediate superclass. super also works perfectly well to invoke overridden methods that are defined further up the class hierarchy. This is because the overridden method is inherited by the immediate superclass, and so the super syntax does in fact refer to the correct method.

To make this more concrete, suppose class A defines method f, and that B is a subclass of A, and that C is a subclass of B that overrides method f.

A(f) --- B --- C(f)
Then you can still use:

super.f()
to invoke the overridden nethod from within class C. This is so because class B inherits method f from class A. If classes A, B and C all define method f, however, then calling super.f() in class C invokes class B's definition of the method. In this case, there is no way to invoke A.f() from within class C because super.super.f() is not legal Java syntax.

It is important to note that super can only be used to invoke overridden methods from within the class that does the overriding. With our Circle and Ellipse classes, for example, there is no way to write a program (with or without super) that invokes the Circle area(); method on an object of type Ellipse. The only way to do this is to use super in a method within the Ellipse class.

Finally, note that this form of super does not have to occur in the first statement in a method, as it does when used to invoke a superclass constructor method.

Data Hiding and Encapsulation

We started these review notes by describing a class as "a collection of data and methods". One of the important object-oriented techniques that we haven't discussed so far is hiding the data within the class, and making it available only through the methods. This technique is often known as encapsulation, because it seals the classes's data (and internal methods) safely inside the "capsule" of the class, where it can be accessed only by trusted users, i.e., by the methods of the class.

Why would you want to do this? The most important reason is to hide the internal implementation details of your class. If you prevent programmers from relying on those details, then you can safely modify the implementation without worrying that you will break existing code that uses the class.

Another reason for encapsulation is to protect your class against accidental or willful stupidity. A class often contains a number of variables that are interdependent and must be in a consistent state. If you allow a programmer (this may be you yourself) to manipulate those variables directly, (s)he may change one variable without changing important related variables, thus leaving the class in an inconsistent state. If, instead, (s)he had to call a method to change the variable, the method can be sure to do everything necessary to keep the state consistent.

Here's another way to think about encapsulation When all of a class's variables are hidden, the class's methods define the only possible operations that can be performed on objects of that class. Once you have carefully tested and debugged your methods, you can be confident that the class will work as expected. On the other hand, if all the variables can be directly manipulated, the number of possibilities you have to test becomes unmanageable.

There are other reasons to hide data, too:

Visibility Modifiers

In most of our examples so far, you've probably noticed the public keyword being used When applied to a class, it means that the class is visible everywhere. When applied to a method or a variable, it means that the method or variable is visible everywhere the class is.

To hide variables (or methods for that matter) you just have to declare them private:

public class Laundromat {     // People can use this class.
  private Laundry[] dirty;    // They can't see this internal variable,
  public void wash() { ... }  // but they can use these public methods
  public void dry() { ... }   // to manipulate the internal variable. 
}
A private field of a class is visible only in methods defined within that class. (We do not discuss inner classes in A201, at least not that much). Similarly, a private method may only be invoked by methods within the class. Private members are not visible within subclasses, and are not inherited by subclasses as other members are. Of course, non-private methods that invoke private methods internally are inherited and may be invoked by subclasses.

Besides public and private, Java has two other visibility levels: protected and the default visibility level, "package visibility", which applies if none of the public, private and protected keywords are used. These visibility levels are discussed below.

A protected member of a class is visible within the class where it is defined, of course, and within all subclasses of the class, and also within all classes that are in the same package as that class. You should use protected visibility when you want to hide fields and methods from code that uses your class, but want those fields and methods to be fully accessible to code that extends your class.

The default package visibility is more restrictive than protected, but less restrictive than private. If a class member is not declared with any of the public, private or protected keywords, then it is visible only within the class that defines it and within classes that are part of the same package. It is not visible to subclasses unless those subclasses are part of the same package.

A note about packages: A package is a group of related and possibly cooperating classes. All non-private members of all classes in the package are visible to all other classes in the package. This is OK because the classes are assumed to know about, and trust each other. The only time difficulty arises is when you write programs without a package statement. These classes are thrown into a default package with other package-less classes, and all their non-private members are visible throughout the package (The default package usually consists of all classes in the current working directory, and that's what we do in this class).

(The rest of this Visibility Modifiers section for your reference only).

There is an important point to make about subclass access to protected members. A subclass inherits the protected members of its superclass, but it can only access those members through instances of itself, not directly in instances of the superclass. Suppose, for example, that A, B, and C are public classes, each defined in a different package, and that a, b, and c are instances of those classes. Let B be a subclass of A, and C be a subclass of B. Now, if A has a protected field x, then the class B inherits that field, and its method can use this.x, b.x, and c.x. But it cannot access a.x. Similarly, if A has a protected method f(), then the methods of class B can invoke this.f(), b.f(), and c.f(), but they cannot invoke a.f().

The following table shows the circumstances under which class members of the various visibility types are accessible to other classes:

Accessible to Member visibility
public protected package private
Same class yes yes yes yes
Class in same package yes no yes no
Subclass (perhaps in different package) yes yes no no
Non-subclass, different package yes no no no

The details of member visibility in Java can become quite confusing. Here are some simple rules of thumb for using visibility modifiers:

Note that you can't take advantage of package visibility unless you use the package statement to group your related classes into packages.

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. The example that follows 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.

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; /* a-ha! */ 
  } 

  // Declare these trivial methods final so we don't get dynamic 
  // method lookup and so that they can be optimized by the compiler. 
  public final double getX () { return x;}
  public final double getY () { return y;}
  public final double getRadius () { return r;}
}
And here's the last part of this overview.

Abstract Classes and Methods: A Brief Tutorial

An abstract method has no body, only a signature followed by a semicolon.

For example:

abstract double area(); 

Any class with an abstract method is automatically abstract itself, and must be declared as such. So we need the blue keyword below, it just has to be there:

abstract class Shape {
  abstract double area(); 
}

A class may be declared abstract even if it has no abstract methods. This prevents it from being instantiated.

For example:

oldschool.cs.indiana.edu%ls -l
total 1
-rw-------   1 dgerman       134 Jul 16 12:28 Example.java
oldschool.cs.indiana.edu%cat Example.java
abstract class Shape {
  double area() { return -1; } 
  public static void main(String[] args) {
    Shape a = new Shape();  
  } 
}
oldschool.cs.indiana.edu%javac Example.java
Example.java:4: class Shape is an abstract class. It can't be instantiated.
    Shape a = new Shape();  
              ^
1 error
oldschool.cs.indiana.edu%

An abstract class cannot be instantiated.

This could be seen in the example above.

It can however have a main method with no problem:

oldschool.cs.indiana.edu%ls -l
total 1
-rw-------   1 dgerman       172 Jul 16 12:33 Example.java
oldschool.cs.indiana.edu%cat Example.java
abstract class Shape {
  double area() { return -1; } 
  public static void main(String[] args) {
    System.out.println("Hello!"); 
    // Shape a = new Shape();  
  } 
}
oldschool.cs.indiana.edu%javac Example.java
oldschool.cs.indiana.edu%java Shape
Hello!
oldschool.cs.indiana.edu%

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.

Here's an example that does that and notice the inherited variables too.

oldschool.cs.indiana.edu%ls -l
total 1
-rw-------   1 dgerman       511 Jul 16 12:40 Example.java
oldschool.cs.indiana.edu%cat Example.java
abstract class Shape {
  int x, y; 
  abstract double area(); 
}
 
class Circle extends Shape {
  int radius; 
  double area() { return 2 * Math.PI * radius * radius; }
  double manhattanDistanceToOrigin() {
    return Math.abs(x) + Math.abs(y);  
  }   
} 
 
class Tester {
  public static void main(String[] args) {
    Circle c = new Circle(); 
    c.radius = 10; 
    c.x = -3; c.y = 5; 
    System.out.println("Area is " + c.area() + 
      " and the distance is " + c.manhattanDistanceToOrigin()); 
  } 
} 
oldschool.cs.indiana.edu%javac Example.java
oldschool.cs.indiana.edu%java Tester
Area is 628.3185307179587 and the distance is 8.0
oldschool.cs.indiana.edu%
If a subclass of an abstract class does not implement all of the abstract methods it inherits, that subclass is itself abstract.

To see this in the code above remove the definition of area() in class Circle and recompile:

oldschool.cs.indiana.edu%ls -l
total 1
-rw-------   1 dgerman       577 Jul 16 12:44 Example.java
oldschool.cs.indiana.edu%cat Example.java
abstract class Shape {
  int x, y; 
  abstract double area(); 
}
 
class Circle extends Shape {
  int radius; 
  /*** let's take area() out, see if it still compiles: 
  double area() { return 2 * Math.PI * radius * radius; }
  ****/ 
  double manhattanDistanceToOrigin() {
    return Math.abs(x) + Math.abs(y);  
  }   
} 
 
class Tester {
  public static void main(String[] args) {
    Circle c = new Circle(); 
    c.radius = 10; 
    c.x = -3; c.y = 5; 
    System.out.println("Area is " + c.area() + 
      " and the distance is " + c.manhattanDistanceToOrigin()); 
  } 
} 
oldschool.cs.indiana.edu%javac Example.java
Example.java:6: class Circle must be declared abstract. It does not define double area() from class Shape.
class Circle extends Shape {
      ^
Example.java:18: class Circle is an abstract class. It can't be instantiated.
    Circle c = new Circle(); 
               ^
2 errors
oldschool.cs.indiana.edu%

It doesn't compile, and for two reasons, not just one.

But the two reasons are closely related.

I hope you enjoyed this tutorial.

I strongly hope the information presented in it was quite manageable.

Please let me know if you have any questions.


Last updated: Apr 12, 2003 by Adrian German for A201