Using Abstract Classes: Responsibility Because They Have No Implementation Specified in The Superclass. Thus
Using Abstract Classes: Responsibility Because They Have No Implementation Specified in The Superclass. Thus
There are situations in which you will want to define a superclass that declares the
structure of a given abstraction without providing a complete implementation of every
method. That is, sometimes you will want to create a superclass that only defines a
generalized form that will be shared by all of its subclasses, leaving it to each subclass
to fill in the details. Such a class determines the nature of the methods that the
subclasses must implement. One way this situation can occur is when a superclass
is unable to create a meaningful implementation for a method. This is the case with
the class Figure used in the preceding example. The definition of area( ) is simply a
placeholder. It will not compute and display the area of any type of object.
As you will see as you create your own class libraries, it is not uncommon for a
method to have no meaningful definition in the context of its superclass. You can
handle this situation two ways. One way, as shown in the previous example, is to
simply have it report a warning message. While this approach can be useful in certain
situations—such as debugging—it is not usually appropriate. You may have methods
which must be overridden by the subclass in order for the subclass to have any meaning.
Consider the class Triangle. It has no meaning if area( ) is not defined. In this case, you
want some way to ensure that a subclass does, indeed, override all necessary methods.
Java’s solution to this problem is the abstract method.
You can require that certain methods be overridden by subclasses by specifying
the abstract type modifier. These methods are sometimes referred to as subclasser
responsibility because they have no implementation specified in the superclass. Thus,
a subclass must override them—it cannot simply use the version defined in the
superclass. To declare an abstract method, use this general form:
abstract type name(parameter-list);
As you can see, no method body is present.
Any class that contains one or more abstract methods must also be declared
abstract. To declare a class abstract, you simply use the abstract keyword in front of the
class keyword at the beginning of the class declaration. There can be no objects of an
abstract class. That is, an abstract class cannot be directly instantiated with the new
operator. Such objects would be useless, because an abstract class is not fully defined.
Also, you cannot declare abstract constructors, or abstract static methods. Any subclass
of an abstract class must either implement all of the abstract methods in the superclass,
or be itself declared abstract.
Here is a simple example of a class with an abstract method, followed by a class
which implements that method:
216 J a v a ™ 2 : T h e C o m p l e t e R e f e r e n c e
C h a p t e r 8 : I n h e r i t a n c e 217
THE JAVA LANGUAGE
// A Simple demonstration of abstract.
abstract class A {
abstract void callme();
// concrete methods are still allowed in abstract classes
void callmetoo() {
System.out.println("This is a concrete method.");
}
}
class B extends A {
void callme() {
System.out.println("B's implementation of callme.");
}
}
class AbstractDemo {
public static void main(String args[]) {
B b = new B();
b.callme();
b.callmetoo();
}
}
Notice that no objects of class A are declared in the program. As mentioned, it is
not possible to instantiate an abstract class. One other point: class A implements a
concrete method called callmetoo( ). This is perfectly acceptable. Abstract classes can
include as much implementation as they see fit.
Although abstract classes cannot be used to instantiate objects, they can be used
to create object references, because Java’s approach to run-time polymorphism is
implemented through the use of superclass references. Thus, it must be possible to
create a reference to an abstract class so that it can be used to point to a subclass object.
You will see this feature put to use in the next example.
Using an abstract class, you can improve the Figure class shown earlier. Since
there is no meaningful concept of area for an undefined two-dimensional figure, the
following version of the program declares area( ) as abstract inside Figure. This, of
course, means that all classes derived from Figure must override area( ).
// Using abstract methods and classes.
abstract class Figure {
double dim1;
double dim2;
Figure(double a, double b) {
dim1 = a;
dim2 = b;
}
// area is now an abstract method
abstract double area();
}
class Rectangle extends Figure {
Rectangle(double a, double b) {
super(a, b);
}
// override area for rectangle
double area() {
System.out.println("Inside Area for Rectangle.");
return dim1 * dim2;
}
}
class Triangle extends Figure {
Triangle(double a, double b) {
super(a, b);
}
// override area for right triangle
double area() {
System.out.println("Inside Area for Triangle.");
return dim1 * dim2 / 2;
}
}
class AbstractAreas {
public static void main(String args[]) {
// Figure f = new Figure(10, 10); // illegal now
Rectangle r = new Rectangle(9, 5);
Triangle t = new Triangle(10, 8);
other subordinate packages. Classes act as containers for data and code. The class is
Java’s smallest unit of abstraction. Because of the interplay between classes and
packages, Java addresses four categories of visibility for class members:
■ Subclasses in the same package
■ Non-subclasses in the same package
■ Subclasses in different packages
■ Classes that are neither in the same package nor subclasses
The three access specifiers, private, public, and protected, provide a variety of
ways to produce the many levels of access required by these categories. Table 9-1 sums
up the interactions.
While Java’s access control mechanism may seem complicated, we can simplify it as
follows. Anything declared public can be accessed from anywhere. Anything declared
private cannot be seen outside of its class. When a member does not have an explicit
access specification, it is visible to subclasses as well as to other classes in the same
package. This is the default access. If you want to allow an element to be seen outside
your current package, but only to classes that subclass your class directly, then declare
that element protected.
Table 9-1 applies only to members of classes. A class has only two possible access
levels: default and public. When a class is declared as public, it is accessible by any
other code. If a class has default access, then it can only be accessed by other code
within its same package.
Private No modifier Protected Public
Same class Yes Yes Yes Yes
Same package
subclass
No Yes Yes Yes
Same package
non-subclass
No Yes Yes Yes
Different
package
subclass
No No Yes Yes
Different
package
non-subclass
No No No Yes
Table 9-1. Class Member Access
Understanding static
There will be times when you will want to define a class member that will be used
independently of any object of that class. Normally a class member must be accessed
only in conjunction with an object of its class. However, it is possible to create a
member that can be used by itself, without reference to a specific instance. To create
such a member, precede its declaration with the keyword static. When a member is
declared static, it can be accessed before any objects of its class are created, and without
reference to any object. You can declare both methods and variables to be static. The
most common example of a static member is main( ). main( ) is declared as static
because it must be called before any objects exist.
Instance variables declared as static are, essentially, global variables. When objects
of its class are declared, no copy of a static variable is made. Instead, all instances of the
class share the same static variable.
Methods declared as static have several restrictions:
■ They can only call other static methods.
■ They must only access static data.
■ They cannot refer to this or super in any way. (The keyword super relates to
inheritance and is described in the next chapter.)
If you need to do computation in order to initialize your static variables, you can
declare a static block which gets executed exactly once, when the class is first loaded.
The following example shows a class that has a static method, some static variables,
and a static initialization block:
// Demonstrate static variables, methods, and blocks.
class UseStatic {
C h a p t e r 7 : A C l o s e r L o o k a t M e t h o d s a n d C l a s s e s 177
THE JAVA LANGUAGE
static int a = 3;
static int b;
static void meth(int x) {
System.out.println("x = " + x);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
static {
System.out.println("Static block initialized.");
b = a * 4;
}
public static void main(String args[]) {
meth(42);
}
}
As soon as the UseStatic class is loaded, all of the static statements are run. First,
a is set to 3, then the static block executes (printing a message), and finally, b is
initialized to a * 4 or 12. Then main( ) is called, which calls meth( ), passing 42 to x.
The three println( ) statements refer to the two static variables a and b, as well as
to the local variable x.
It is illegal to refer to any instance variables inside of a static method.
Here is the output of the program:
Static block initialized.
x = 42
a=3
b = 12
Outside of the class in which they are defined, static methods and variables can be
used independently of any object. To do so, you need only specify the name of their
class followed by the dot operator. For example, if you wish to call a static method
from outside its class, you can do so using the following general form:
classname.method( )
Here, classname is the name of the class in which the static method is declared.
As you can see, this format is similar to that used to call non-static methods through
object- reference variables. A static variable can be accessed in the same way—by use
of the dot operator on the name of the class. This is how Java implements a controlled
version of global methods and global variables.
Here is an example. Inside main( ), the static method callme( ) and the static
variable b are accessed outside of their class.
class StaticDemo {
static int a = 42;
static int b = 99;
static void callme() {
System.out.println("a = " + a);
}
}
class StaticByName {
public static void main(String args[]) {
StaticDemo.callme();
System.out.println("b = " + StaticDemo.b);
}
}
Here is the output of this program:
a = 42
b = 99
Introducing final
A variable can be declared as final. Doing so prevents its contents from being
modified. This means that you must initialize a final variable when it is declared.
(In this usage, final is similar to const in C/C++/C#.) For example:
final int