Team LiB
Previous Section Next Section

2.10. Packages and the Java Namespace

A package is a named collection of classes, interfaces, and other reference types. Packages serve to group related classes and define a namespace for the classes they contain.

The core classes of the Java platform are in packages whose names begin with java. For example, the most fundamental classes of the language are in the package java.lang. Various utility classes are in java.util. Classes for input and output are in java.io, and classes for networking are in java.net. Some of these packages contain subpackages, such as java.lang.reflect and java.util.regex. Extensions to the Java platform that have been standardized by Sun typically have package names that begin with javax. Some of these extensions, such as javax.swing and its myriad subpackages, were later adopted into the core platform itself. Finally, the Java platform also includes several "endorsed standards," which have packages named after the standards body that created them, such as org.w3c and org.omg.

Every class has both a simple name, which is the name given to it in its definition, and a fully qualified name, which includes the name of the package of which it is a part. The String class, for example, is part of the java.lang package, so its fully qualified name is java.lang.String.

This section explains how to place your own classes and interfaces into a package and how to choose a package name that won't conflict with anyone else's package name. Next, it explains how to selectively import type names into the namespace so that you don't have to type the package name of every class or interface you use. Finally, the section explains a feature that is new in Java 5.0: the ability to import static members of types into the namespace so that you don't need to prefix these with a package name or a class name.

2.10.1. Package Declaration

To specify the package a class is to be part of, you use a package declaration. The package keyword, if it appears, must be the first token of Java code (i.e., the first thing other than comments and space) in the Java file. The keyword should be followed by the name of the desired package and a semicolon. Consider a Java file that begins with this directive:

package com.davidflanagan.examples;

All classes defined by this file are part of the package com.davidflanagan.examples.

If no package directive appears in a Java file, all classes defined in that file are part of an unnamed default package. In this case, the qualified and unqualified names of a class are the same. The possibility of naming conflicts means that you should use this default package only for very simple code or early on in the development process of a larger project.

2.10.2. Globally Unique Package Names

One of the important functions of packages is to partition the Java namespace and prevent name collisions between classes. It is only their package names that keep the java.util.List and java.awt.List classes distinct, for example. In order for this to work, however, package names must themselves be distinct. As the developer of Java, Sun controls all package names that begin with java , javax, and sun.

For the rest of us, Sun proposes a package-naming scheme, which, if followed correctly, guarantees globally unique package names. The scheme is to use your Internet domain name, with its elements reversed, as the prefix for all your package names. My web site is at http://davidflanagan.com, so all my Java packages begin with com.davidflanagan. It is up to me to decide how to partition the namespace below com.davidflanagan, but since I own that domain name, no other person or organization who is playing by the rules can define a package with the same name as any of mine.

Note that these package-naming rules apply primarily to API developers. If other programmers will be using classes that you develop along with unknown other classes, it is important that your package name be globally unique. On the other hand, if you are developing a Java application and will not be releasing any of the classes for reuse by others, you know the complete set of classes that your application will be deployed with and do not have to worry about unforeseen naming conflicts. In this case, you can choose a package naming scheme for your own convenience rather than for global uniqueness. One common approach is to use the application name as the main package name (it may have subpackages beneath it).

2.10.3. Importing Types

When referring to a class or interface in your Java code, you must, by default, use the fully qualified name of the type, including the package name. If you're writing code to manipulate a file and need to use the File class of the java.io package, you must type java.io.File. This rule has three exceptions:

  • Types from the package java.lang are so important and so commonly used that they can always be referred to by their simple names.

  • The code in a type p.T may refer to other types defined in the package p by their simple names.

  • Types that have been imported into the namespace with an import declaration may be referred to by their simple names.

The first two exceptions are known as "automatic imports." The types from java.lang and the current package are "imported" into the namespace so that they can be used without their package name. Typing the package name of commonly used types that are not in java.lang or the current package quickly becomes tedious, and so it is also possible to explicitly import types from other packages into the namespace. This is done with the import declaration.

import declarations must appear at the start of a Java file, immediately after the package declaration, if there is one, and before any type definitions. You may use any number of import declarations in a file. An import declaration applies to all type definitions in the file (but not to any import declarations that follow it).

The import declaration has two forms. To import a single type into the namespace, follow the import keyword with the name of the type and a semicolon:

import java.io.File;    // Now we can type File instead of java.io.File

This is known as the "single type import" declaration.

The other form of import is the "on-demand type import." In this form, you specify the name of a package followed the characters .* to indicate that any type from that package may be used without its package name. Thus, if you want to use several other classes from the java.io package in addition to the File class, you can simply import the entire package:

import java.io.*;   // Now we can use simple names for all classes in java.io

This on-demand import syntax does not apply to subpackages. If I import the java.util package, I must still refer to the java.util.zip.ZipInputStream class by its fully qualified name.

Using an on-demand type import declaration is not the same as explicitly writing out a single type import declaration for every type in the package. It is more like an explicit single type import for every type in the package that you actually use in your code. This is the reason it's called "on demand"; types are imported as you use them.

2.10.3.1 Naming conflicts and shadowing

import declarations are invaluable to Java programming. They do expose us to the possibility of naming conflicts, however. Consider the packages java.util and java.awt. Both contain types named List. java.util.List is an important and commonly used interface. The java.awt package contains a number of important types that are commonly used in client-side applications, but java.awt.List has been superseded and is not one of these important types. It is illegal to import both java.util.List and java.awt.List in the same Java file. The following single type import declarations produce a compilation error:

import java.util.List;
import java.awt.List;

Using on-demand type imports for the two packages is legal:

import java.util.*;  // For collections and other utilities.
import java.awt.*;   // For fonts, colors, and graphics.

Difficulty arises, however, if you actually try to use the type List. This type can be imported "on demand" from either package, and any attempt to use List as an unqualified type name produces a compilation error. The workaround, in this case, is to explicitly specify the package name you want.

Because java.util.List is much more commonly used than java.awt.List, it is useful to combine the two on-demand type import declarations with a single-type import declaration that serves to disambiguate what we mean when we say List:

import java.util.*;    // For collections and other utilities.
import java.awt.*;     // For fonts, colors, and graphics.
import java.util.List; // To disambiguate from java.awt.List

With these import declarations in place, we can use List to mean the java.util.List interface. If we actually need to use the java.awt.List class, we can still do so as long as we include its package name. There are no other naming conflicts between java.util and java.awt, and their types will be imported "on demand" when we use them without a package name.

2.10.4. Importing Static Members

In Java 5.0 and later, you can import the static members of types as well as types themselves using the keywords import static. (Static members are explained in Chapter 3. If you are not already familiar with them, you may want to come back to this section later.) Like type import declarations, these static import declarations come in two forms: single static member import and on-demand static member import. Suppose, for example, that you are writing a text-based program that sends a lot of output to System.out. In this case, you might use this single static member import to save yourself typing:

import static java.lang.System.out;

With this import in place, you can then use out.print( ) instead of System.out.print( ). Or suppose you are writing a program that uses many of the the trigonometric and other functions of the Math class. In a program that is clearly focused on numerical methods like this, having to repeatedly type the class name "Math" does not add clarity to your code; it just gets in the way. In this case, an on-demand static member import may be appropriate:

import static java.lang.Math.*

With this import declaration, you are free to write concise expressions like sqrt(abs(sin(x))) without having to prefix the name of each static method with the class name Math.

Another important use of import static declarations is to import the names of constants into your code. This works particularly well with enumerated types (see Chapter 4). Suppose, for example that you want to use the values of this enumerated type in code you are writing:

package climate.temperate;
enum Seasons { WINTER, SPRING, SUMMER, AUTUMN };

You could import the type climate.temperate.Seasons and then prefix the constants with the type name: Seasons.SPRING. For more concise code, you could import the enumerated values themselves:

import static climate.temperate.Seasons.*;

Using static member import declarations for constants is generally a better technique than implementing an interface that defines the constants.

2.10.4.1 Static member imports and overloaded methods

A static import declaration imports a name, not any one specific member with that name. Since Java allows method overloading and allows a type to have fields and methods with the same name, a single static member import declaration may actually import more than one member. Consider this code:

import static java.util.Arrays.sort;

This declaration imports the name "sort" into the namespace, not any one of the 19 sort( ) methods defined by java.util.Arrays. If you use the imported name sort to invoke a method, the compiler will look at the types of the method arguments to determine which method you mean.

It is even legal to import static methods with the same name from two or more different types as long as the methods all have different signatures. Here is one natural example:

import static java.util.Arrays.sort;
import static java.util.Collections.sort;

You might expect that this code would cause a syntax error. In fact, it does not because the sort( ) methods defined by the Collections class have different signatures than all of the sort( ) methods defined by the Arrays class. When you use the name "sort" in your code, the compiler looks at the types of the arguments to determine which of the 21 possible imported methods you mean.

    Team LiB
    Previous Section Next Section