Posted on Leave a comment

Importing Static Members of Reference Types – Access Control

Importing Static Members of Reference Types

Analogous to the type import facility, Java also allows import of static members of reference types from packages, often called static import. Imported static members can be used by their simple names, and therefore need not be qualified. Importing static members of reference types from the unnamed package is not permissible.

The two forms of static import are shown here:

  • Single static import: imports a specific static member from the designated type

import static fully_qualified_type_name.static_member_name;

  • Static import on demand: imports all static members in the designated type

import static fully_qualified_type_name.*;

Both forms require the use of the keyword import followed by the keyword static, although the feature is called static import. In both cases, the fully qualified name of the reference type we are importing from is required.

The first form allows single static import of individual static members, and is demonstrated in Example 6.2. The constant PI, which is a static field in the class java.lang.Math, is imported at (1). Note the use of the fully qualified name of the type in the static import statement. The static method named sqrt from the class java.lang.Math is imported at (2). Only the name of the static method is specified in the static import statement; no parameters are listed. Use of any other static member from the Math class requires that the fully qualified name of the class be specified. Since types from the java.lang package are imported implicitly, the fully qualified name of the Math class is not necessary, as shown at (3).

Static import on demand is easily demonstrated by replacing the two import statements in Example 6.2 with the following import statement:

Click here to view code image

import static java.lang.Math.*;

We can also dispense with the use of the class name Math at (3), as all static members from the Math class are now imported:

Click here to view code image

double hypotenuse = hypot(x, y);   // (3′) Type name can now be omitted.

Example 6.2 Single Static Import

Click here to view code image

import static java.lang.Math.PI;           // (1) Static field
import static java.lang.Math.sqrt;         // (2) Static method
// Only specified static members are imported.
public class Calculate3 {
  public static void main(String[] args) {
    double x = 3.0, y = 4.0;
double squareroot = sqrt(y);           // Simple name of static method
    double hypotenuse = Math.hypot(x, y);  // (3) Requires type name
    double area = PI * y * y;              // Simple name of static field
    System.out.printf(“Square root: %.2f, hypotenuse: %.2f, area: %.2f%n”,
                        squareroot, hypotenuse, area);
  }
}

Output from the program:

Click here to view code image

Square root: 2.00, hypotenuse: 5.00, area: 50.27

Example 6.3 illustrates how static import can be used to access interface constants (§5.6, p. 254). The static import statement at (1) allows the interface constants in the package mypkg to be accessed by their simple names. The static import facility avoids the MyFactory class having to implement the interface so as to access the constants by their simple name (often referred to as the interface constant antipattern):

Click here to view code image

public class MyFactory implements mypkg.IMachineState {
 // …
}

Example 6.3 Avoiding the Interface Constant Antipattern

Click here to view code image

package mypkg;
public interface IMachineState {
  // Fields are public, static, and final.
  int BUSY = 1;
  int IDLE = 0;
  int BLOCKED = -1;
}

Click here to view code image

import static mypkg.IMachineState.*;    // (1) Static import interface constants
public class MyFactory {
  public static void main(String[] args) {
    int[] states = { IDLE, BUSY, IDLE, BLOCKED }; // (2) Access by simple name
    for (int s : states)
      System.out.print(s + ” “);
  }
}

Output from the program:

0 1 0 -1

Static import is ideal for importing enum constants from packages, as such constants are static members of an enum type (§5.13, p. 287). Example 6.4 combines type and static imports. The enum constants can be accessed at (5) using their simple names because of the static import statement at (2). The type import at (1) is required to access the enum type State by its simple name at (4) and (6).

Example 6.4 Importing Enum Constants

Click here to view code image

package mypkg;
public enum State { BUSY, IDLE, BLOCKED }

Click here to view code image

// File: Factory.java (in unnamed package)
import mypkg.State;                  // (1) Single type import
import static mypkg.State.*;         // (2) Static import on demand
import static java.lang.System.out;  // (3) Single static import
public class Factory {
  public static void main(String[] args) {
    State[] states = {               // (4) Using type import implied by (1)
        IDLE, BUSY, IDLE, BLOCKED    // (5) Using static import implied by (2)
    };
    for (State s : states)           // (6) Using type import implied by (1)
      out.print(s + ” “);            // (7) Using static import implied by (3)
  }
}

Output from the program:

IDLE BUSY IDLE BLOCKED

Identifiers in a class can shadow static members that are imported. Example 6.5 illustrates the case where the parameter out of the method writeInfo() has the same name as the statically imported field java.lang.System.out. The type of the parameter out is ShadowImport and that of the statically imported field out is PrintStream. Both classes PrintStream and ShadowImport define the method println() that is called in the program. The only way to access the imported field out in the method write-Info() is to use its fully qualified name.

Example 6.5 Shadowing Static Import

Click here to view code image

import static java.lang.System.out;       // (1) Static import
public class ShadowImport {
  public static void main(String[] args) {
    out.println(“Calling println() in java.lang.System.out”);
    ShadowImport sbi = new ShadowImport();
    writeInfo(sbi);
  }
// Parameter out shadows java.lang.System.out:
  public static void writeInfo(ShadowImport out) {
    out.println(“Calling println() in the parameter out”);
    System.out.println(“Calling println() in java.lang.System.out”); // Qualify
  }
  public void println(String msg) {
    out.println(msg + ” of type ShadowImport”);
  }
}

Output from the program:

Click here to view code image

Calling println() in java.lang.System.out
Calling println() in the parameter out of type ShadowImport
Calling println() in java.lang.System.out

The next code snippet illustrates a common conflict that occurs when a static field with the same name is imported by several static import statements. This conflict is readily resolved by using the fully qualified name of the field. In the case shown here, we can use the simple name of the class in which the field is declared, as the java.lang package is implicitly imported by all compilation units.

Click here to view code image

import static java.lang.Integer.MAX_VALUE;
import static java.lang.Double.MAX_VALUE;
public class StaticFieldConflict {
  public static void main(String[] args) {
    System.out.println(MAX_VALUE);          // (1) Ambiguous! Compile-time error!
    System.out.println(Integer.MAX_VALUE);  // OK
    System.out.println(Double.MAX_VALUE);   // OK
  }
}

Conflicts can also occur when a static method with the same signature is imported by several static import statements. In Example 6.6, a method named binarySearch is imported 21 times by the static import statements. This method is overloaded twice in the java.util.Collections class and 18 times in the java.util.Arrays class, in addition to one declaration in the mypkg.Auxiliary class. The classes java.util.Arrays and mypkg.Auxiliary have a declaration of this method with the same signature (binarySearch(int[], int) that matches the method call at (2), resulting in a signature conflict that is flagged as a compile-time error. The conflict can again be resolved by specifying the fully qualified name of the method.

If the static import statement at (1) is removed, there is no conflict, as only the class java.util.Arrays has a method that matches the method call at (2). If the declaration of the method binarySearch() at (3) is allowed, there is also no conflict, as this method declaration will shadow the imported method whose signature it matches.

Example 6.6 Conflict in Importing a Static Method with the Same Signature

Click here to view code image

package mypkg;
public class Auxiliary {
  public static int binarySearch(int[] a, int key) { // Same in java.util.Arrays
    // Implementation is omitted.
    return -1;
  }
}

Click here to view code image

// File: MultipleStaticImport.java (in unnamed package)
import static java.util.Collections.binarySearch;  //    2 overloaded methods
import static java.util.Arrays.binarySearch;       // + 18 overloaded methods
import static mypkg.Auxiliary.binarySearch; // (1) Causes signature conflict
public class MultipleStaticImport {
  public static void main(String[] args) {
    int index = binarySearch(new int[] {10, 50, 100}, 50); // (2) Ambiguous!
    System.out.println(index);
  }
//public static int binarySearch(int[] a, int key) {       // (3)
//  return -1;
//}
}

Leave a Reply

Your email address will not be published. Required fields are marked *