Chapter 18 Packages

1) Packages define units of software that can be distributed independently and combined with other packages to form applications.

  If a type is not declared as being part of an explicit package, it is placed in an unnamed package. Each system must support at least one unnamed package, but they can support moretypically one per class loader. The existence of the unnamed package makes it simple for you to write small programs without being encumbered by the organizational requirements imposed on the members of named packages. Any non-trivial program or set of library classes should always be part of a named package.

 

2) A good way to ensure unique package names is to use an Internet domain name. If you worked at a company named Magic, Inc., that owned the domain name magic.com, the attribute package declaration should be

package com.magic.attr;

Notice that the components of the domain name are reversed from the normal domain name convention.

 

3) You can't declare your own Vector class and import java.util.Vector. Nor can you import the same type name from two different packages using two single type imports.

  The package and import mechanisms give programmers control over potentially conflicting names. If a package used for another purpose, such as linguistics, has a class called Attributed for language attributes, programmers who want to use both types in the same source file have several options:

  • Refer to all types by their fully qualified names, such as attr.Attributed and lingua.Attributed.

  • Import only attr.Attributed or attr.*, use the simple name Attributed for attr.Attributed, and use the full name of lingua.Attributed.

  • Do the converse: import lingua.Attributed or lingua.*, use the simple name Attributed for lingua.Attributed, and use the full name of attr.Attributed.

  • Import all of both packagesattr.* and lingua.*and use the fully qualified names attr.Attributed and lingua.Attributed in your code. (If a type with the same name exists in two packages imported on demand, you cannot use the simple name of either type.)

 

3) A class member that is not declared public, protected, or private can be used by any code within the package but is hidden outside the package(even for the Subclasses outside the package). In other words, the default access for an identifier is "package" except for members of interfaces, which are implicitly public.

  Every type has therefore three different contracts that it defines:

  • The public contract, defining the primary functionality of the type.

  • The protected contract, defining the functionality available to subtypes for specialization purposes.

  • The package contract, defining the functionality available within the package to effect cooperation between package types.

  A method can be overridden in a subclass only if the method is accessible in the superclass. If the method is not accessible in the superclass then the method in the subclass does not override the method in the superclass even if it has the same signature. When a method is invoked at runtime the system has to consider the accessibility of the method when deciding which implementation of the method to run.

The following contrived example should make this clearer. Suppose we have a class AbstractBase declared in package P1:

package P1;

public abstract class AbstractBase {
    private   void pri() { print("AbstractBase.pri()"); }
              void pac() { print("AbstractBase.pac()"); }
    protected void pro() { print("AbstractBase.pro()"); }
    public    void pub() { print("AbstractBase.pub()"); }

    public final void show() {
        pri();
        pac();
        pro();
        pub();
    }
}

We have four methods, each with a different access modifier, each of which simply identifies itself. The method show invokes each of these methods on the current object. It will show which implementation of each method is invoked when applied to different subclass objects.

Now we define the class Concrete1, which extends AbstractBase but lives in the package P2:

package P2;

import P1.AbstractBase;

public class Concrete1 extends AbstractBase {
    public void pri() { print("Concrete1.pri()"); }
    public void pac() { print("Concrete1.pac()"); }
    public void pro() { print("Concrete1.pro()"); }
    public void pub() { print("Concrete1.pub()"); }
}

This class redeclares each of the methods from the superclass and changes their implementation to report that they are in class Concrete1. It also changes their access to public so that they are accessible to all other code. Executing the code

new Concrete1().show();

produces the following output:

AbstractBase.pri()
AbstractBase.pac()
Concrete1.pro()
Concrete1.pub()

Because the private method pri is inaccessible to subclasses (or to any other class) the implementation from AbstractBase is always the one invoked from show. The package accessible pac method of AbstractBase is not accessible in Concrete1, thus the implementation of pac in Concrete1 does not override that in AbstractBase, so it is AbstractBase.pac that show invokes. Both the pro and pub methods are accessible in Concrete1 and get overridden, so the implementation from Concrete1 is used in show.

Next we define the class Concrete2, which extends Concrete1 but is in the same package P1 as AbstractBase:[2]

[2] Having an inheritance hierarchy that weaves in and out of a package is generally a very bad idea. It is used here purely for illustration.

package P1;

import P2.Concrete1;

public class Concrete2 extends Concrete1 {
    public void pri() { print("Concrete2.pri()"); }
    public void pac() { print("Concrete2.pac()"); }
    public void pro() { print("Concrete2.pro()"); }
    public void pub() { print("Concrete2.pub()"); }
}

Each method of Concrete2 overrides the version from Concrete1 because they are all public and therefore accessible. Also, because Concrete2 is in the same package as AbstractBase, the method AbstractBase.pac is accessible in Concrete2 and so is overridden by Concrete2.pac. Invoking show on a Concrete2 object prints

AbstractBase.pri()
Concrete2.pac()
Concrete2.pro()
Concrete2.pub()

Finally, we define the class Concrete3, which extends Concrete2 but is in a different package P3:

package P3;

import P1.Concrete2;

public class Concrete3 extends Concrete2 {
    public void pri() { print("Concrete3.pri()"); }
    public void pac() { print("Concrete3.pac()"); }
    public void pro() { print("Concrete3.pro()"); }
    public void pub() { print("Concrete3.pub()"); }
}

Invoking show on a Concrete3 object prints

AbstractBase.pri()
Concrete3.pac()
Concrete3.pro()
Concrete3.pub()

Here the method Concrete3.pac appears to have overridden the inaccessible AbstractBase.pac. In fact, Concrete3.pac overrides Concrete2.pac, and Concrete2.pac overrides AbstractBase.pactherefore Concrete3.pac transitively overrides AbstractBase.pac. By redeclaring pac as public, Concrete2 made it accessible and overridable by any subclass.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章