Declaring a Class

   

In general, Java class declarations take the form

  AccessSpecifier Modifiers  class NewClass  extends SuperclassName   implements InterfaceName  

where everything in italics is optional. As you can see, there are five properties of the class that can be defined in the declaration:

  • Access Specifier

  • Modifiers

  • Class name

  • Superclass

  • Interfaces implemented

See Chapter 9, "Interfaces,"

Access Specifiers

The access specifier in a class declaration determines how visible a class is to other classes. The specifiers that apply to classes are similar to the method access specifiers discussed in Chapter 4, " Methods and Exceptions." Although specifiers are not of primary importance while developing an individual class, they become very important when you decide to create other classes, interfaces, and exceptions that use that class.

For now, focus on top-level classes (that is, those not defined within another class declaration). Classes that are not top-level classes are known as inner classes and are discussed later in this chapter. When declaring a top-level class, you can state that the class has public access, which is what you have seen in the examples so far, or you can omit the access specifier and accept the default access restrictions.

Public Classes

Using the public specifier in a class declaration makes the class accessible to all other classes. A public class can be used (for example, as a data type for a class variable or a type for a method parameter) or extended by any class. Here's an example:

 public class PictureFrame 

Also, remember that public classes must be defined in a file called ClassName.java (for example, PictureFrame.java for the preceding declaration).

A second restriction relates to the contents of an individual source file, which has not been discussed yet. Java allows you to define more than one top-level class in a single file as long as no more than one of those classes is declared as public.

Package Classes

If you do do not explicitly declare a class to be public, it will be assigned package access by default.

Packages are the subject of a later section, but you need a quick introduction here to make the definition of package access clear. A package in Java is a grouping of classes defined by the programmer. Packages provide a way to organize your code by placing related classes together. Access restrictions are less stringent between classes within the same package because the intent is for these classes to work together as a cohesive unit. With that said, package access can be distinguished from public access. Whereas any class can use a public class, by omitting the public modifier, you can declare a class for use only by classes within the same package.

Remember that although package is a keyword in Java, you do not use it (or any other access specifier) in a declaration for a class with package access. Here's an example declaration:

 class PictureFrame 

Note

Package access is also known as "default" or "friendly" access. "Default" access is an appropriate description given that this level of access is the default assigned when the public specifier is not present. "Friendly" reflects the fact that package access is not as restrictive as that associated with the private specifier, but also not as accessible as protected.


Modifiers

In addition to an access specifier, you can precede the class name in a declaration with one of two optional modifiers. These modifiers, final and abstract, control the use of a class in an inheritance hierarchy. Note that both modifiers cannot be used in a single class declaration. The reason for this will be clear after the supported modifiers are defined.

Note

There is a third modifier, strictfp, that you can include in a class, interface, or method declaration. This rarely needed modifier instructs the compiler to use the IEEE 754 standard for all floating-point operations. This standard was the default prior to Java 2, but it is not as fast as other implementations that are the default for current JVMs. The one disadvantage of these other implementations is that there is a slight chance of them producing different results across platforms. If you require identical results across all JVMs and platforms, include the strictfp modifier in your declarations.


Final Classes

The final modifier prevents a class from being extended (that is, having any subclasses).

The reason for declaring final classes might not be evident at first. You might be wondering why you would want to prevent other classes from extending your class, especially since inheritance is supposed to be one of the appeals of object-oriented programming.

It is important to remember that the object-oriented approach effectively enables you to create alternate versions of a class (by creating children that inherit its properties and change it somewhat). Consequently, if you create a class to serve as a complete implementation of some particular function (for example, a class that will handle network communications using a specific protocol), you don't want to allow other classes to modify parts of this function because the original intent will be lost. Thus, by making the class final, you eliminate this possibility and ensure consistency. The final modifier gives you a precise way to make your intent clear to other programmers regarding the use of a class.

In addition, the compiler can carry out a number of performance optimizations on a final class that otherwise would not be possible. Polymorphism allows you to write flexible code, but the associated dynamic method calls degrade performance somewhat. If you write code that calls a method on a non-final class instance, the compiler must allow for the possibility that the instance encountered at runtime will actually be an instance of a subclass. This means that the method must be looked up when the code is executed rather than being known when the class is compiled. If, instead, you use a final class, the method call is fully defined during compilation because there is no possibility of a subclass instance being used. Of course, the final modifier should only be used when a class should have no subclasses. The performance advantage is a side effect only, but you should use this as incentive to consider whether your classes should allow subclasses when you are declaring them.

Here's an example declaration for a final class:

 final class PictureFrame 

Access specifiers and modifiers can be used in any order within a class declaration. The following two declarations are the same to the compiler:

 public final class PictureFrame final public class PictureFrame 
Abstract Classes

You can view the abstract modifier as the opposite of final. Whereas final classes cannot be extended, abstract classes must be extended before an instance can be created. The abstract modifier indicates that a class implementation is incomplete and must be added to before it can represent an actual object. A declaration for an abstract class takes the following form:

 abstract class PictureFrame 

A class that you have completely written and compiled can be incomplete depending on how common behavior is isolated and reused in an inheritance hierarchy. The superclass that contains the common behavior in such a hierarchy often does not contain enough behavior to represent a valid object. For example, consider a grammar-checking program that must support multiple written languages. You could proceed by defining an EnglishChecker, a FrenchChecker, and a SpanishChecker class in which each defines the required functionality for a given language. Knowing that some of the methods you need are independent of the language, you define a GrammarChecker class as a common superclass that contains these methods rather than repeating them in each class. This alone does not produce an abstract class, because even though GrammarChecker does not implement all you need for a language, it is nonetheless a complete implementation of the methods it does provide. The advantage in making GrammarChecker an abstract class is to take advantage of polymorphism.

Here, assume that each of your language-specific classes has a checkPunctuation() method that validates the punctuation found in a string. To prevent your program from having to know which language is being used each time you make a call to checkPunctuation(), this method can be declared in GrammarChecker without an implementation (that is, an abstract method), and the language-specific subclasses can provide an appropriate implementation in each case. Having an abstract method forces GrammarChecker to be declared as an abstract class. Even though an instance of GrammarChecker itself cannot be created, it is still a key component of the flexible design needed for this example. This structure supports a clean separation of the language-dependent and language-independent functionality of the program. If a programmer later needed to add a GermanChecker class, a perfect starting point would be to look at the list of abstract methods in GrammarChecker to gain an understanding of what behavior has to be provided to support a new language.

Note

Although most abstract classes contain at least one abstract method, this is not required. You might find cases in which you define a class that has no abstract methods but still does not represent an object that should be instantiated . You can declare the class abstract and require subclassing just as in the preceding example.


Class Name

The name of a Java class must adhere to the same rules as any other identifier. Specifically, class names must

  • Begin with a letter, an underscore (_), or a currency symbol ($, & pound ;, and so on)

  • Contain only letters , digits, underscores, and currency symbols

  • Not be the same as any Java keyword (such as void or int )

Also, it is accepted practice to capitalize the first letter in the name of a class and use mixed case instead of underscores to separate words (for example, MyClassName ).

Although only required for public classes, it is generally a good practice to name the file in which NewClass is defined NewClass.java. Doing so helps the compiler find NewClass, even if NewClass has not been compiled yet.

Superclasses ”Extending Another Class

One of the most important aspects of OOP is the capability to use the methods and fields of a class you have already built. This can be done by declaring a member variable that is a reference to another class (this is known as either aggregation or composition )

or it might involve building upon another class through inheritance and subclassing (this is known as derivation ).

To define an inheritance relationship, you include an extends clause in the declaration of a class, making it a subclass of the named parent class.

Note

Even if you omit the extends clause in a class declaration, any class you declare is still a subclass. The java.lang.Object class, discussed a little later, is at the root of the Java class hierarchy and is the superclass of any class declared without an explicit superclass.


By extending a class, you gain all the functionality of a superclass while providing yourself the opportunity to add new behavior or even modify existing behavior. If you declare a subclass without defining any fields or methods (and use the same access specifier and modifier, if any), the new class will behave identically to its superclass and only differ by its name. The subclass would not be very interesting but it would be a valid class, and it would have all the fields and methods declared in or inherited by it superclass.

Note

Multiple-inheritance is not supported in Java. Thus, unlike C++, Java classes can extend only one class. Interfaces provide Java with a more flexible alternative to multiple inheritance that focuses on behavior rather than a class hierarchy.


How Much Inheritance Is Too Much?

A common mistake in object-oriented programming is to overuse inheritance. Although inheritance is a powerful feature, a class hierarchy can become rigid if inheritance is taken to the extreme. A standard rule of thumb is to apply the "is-a" versus "has-a" test.

For example, a Ford is a car, so a class hierarchy that defines a Ford class as a subclass of a Car class makes sense. This example strictly follows an "is-a" definition, because there is nothing that a Ford does that a generic Car does not do. A Ford is a unique classification of Car that has its own implementation of automotive behavior, but you don't need to add some new capability, such as floating on water, when you declare a Ford class.

Slightly different from an "is-a" justification for inheritance is the "is-like-a" test. In this case, you make use of inheritance to declare a subclass that is similar enough to its superclass to share its interface, but it is also dissimilar in that it requires additional methods to represent its behavior completely. This design is not as clean an approach because treating the subclass as its superclass doesn't allow you to exercise all its functionality, but it can be useful.

If two classes cannot be described as having an "is-a" or "is-like-a" relationship, you should not inherit one from the other. For example, a car has an engine, but it isn't an engine, so Car should not be a subclass of an Engine class. A composition approach should be used here so that Car contains a reference to an Engine and delegates all functionality related to powering the vehicle to that instance.

The appropriate use of inheritance in these simple examples is obvious, but this is not always the case. You should exercise caution when considering inheritance and more often stress composition and delegation in your class designs to make them more flexible and better able to adapt to new requirements during their lifecycles.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net