Pushing Java Envelope – Part 3: Packages & Imports

The art of coding is in fact deciding on where to put a piece of code. It can be either within a file or distributing it to different files. However, putting code into many files create a few problems. First one is name conflicts. Suppose that there are two java class files containing different classes but having the same name. When we refer to that class, we must uniquely specify which one is our preference. There should be no ambiguity. The second problem is access protection. When we define a class, we may want to preserve its use to only related classes. To solve these problems, Java presents the package mechanism.

Declaring Packages

A class may not be a part of a package but for the reasons above, it is highly recommended to do so. If we do not explicitly declare its package, a class is a member of default package. The packages are generally named by reversing the domain name of the companies. In my case, I am using mertersualp.blog as my URL and reversing it will result blog.mertersualp. Hence, my packages will start with this. The dot is the separator. We cannot use dot in our package names. Actually, the package naming rules are exactly the same with variable naming ones. The package name, or a part just after the separating dot, may start with a letter, underscore(_), or dollar sign($). After that, the numbers may also appear with these characters. A small example file with its package declaration is here:

package blog.mertersualp.imports;

public class ImportTests {
  public static final int LIMIT = 10;
  
  public static void main(String[] args) {
    System.out.println("ImportTests main method is running.");
  }
  
  public int nonStaticMethod() {
    System.out.println("ImportTests nonStaticMethod is running.");
    return 2;
  }
}

In Java, there is a clear distinction between the location of the .java source code and the location of the .class file. The .java source code file can be anywhere in the file system. It is only used while compiling. However, the important part is the location of the .class file. To be used properly, it must be “somewhere” under the java class path. In any operating system, we can set the system CLASSPATH variable with one or more file system locations. Another method is providing the preferred path as the class path by using the -cp parameter while compiling the source codes. If we do not explicitly define it, the default class path will be the current directory. Let’s see what happens when the code above is compiled simply with this:

javac ImportTests.java

The result is having the compiled .class file in the same location with the .java source code file. That seems OK until we run it like this:

java ImportTests

Ups… Hitting a roadblock.

roadblock

The problem here is that, java cannot find the class because, for each path in the java class path, it is looking for it under the blog/mertersualp/imports folder. So, one effect of package declaration is that, the .class presents itself under the folder structure defined exactly as the package declaration. There must be a way to put the compiled .class file to its proper place. This can be done by using the -d parameter while compiling the source code. With that, we specify the folder where the packaging structure begins. In our example, the absolute path of the source code is

/Users/merter/Documents/javaSpace/ImportTests.java

The following compilation command will compile this file and put it under the proper place for the class. If the package folders are missing, compiler will create them. But the path provided to the -d parameter must exist beforehand.

javac -d /Users/merter/Documents/javaSpace /Users/merter/Documents/javaSpace/ImportTests.java

This will create the file structure under the /Users/merter/Documents/javaSpace folder. So, the final ImportTests.class file can be found in /Users/merter/Documents/javaSpace/blog/mertersualp/imports/

With all these in place, we can run the .class file in two different ways:

java -cp /Users/merter/Documents/javaSpace blog.mertersualp.imports.ImportTests
java -cp /Users/merter/Documents/javaSpace blog/mertersualp/imports/ImportTests

proper placement

We can compile simultaneously more than one class, which may or may not share the same package. Suppose that we have another class, ImportChildTests.java. It is not in the same package, but its package is under that. In Java terminology, this is a child package. The code is here.

package blog.mertersualp.imports.children;

public class ImportChildTests {
  public static final int CHILD_LIMIT = 5;

  public static void main(String[] args) {
    System.out.println("ImportChildTests main method is running.");
  }
  
  public int nonStaticMethod() {
    System.out.println("ImportChildTests nonStaticMethod is running.");
    return 1;
  }
}

This class is more or less the same with the first one. Assuming that both these source codes are in the same location, and we are also in that folder, we can compile them with the following command:

javac -d . Import*.java

The two paths, /Users/merter/Documents/javaSpace/blog/mertersualp/imports/ and /Users/merter/Documents/javaSpace/blog/mertersualp/imports/children/ are created, if they do not exist already, and the two .class files will be put under their corresponding locations. As we can see, the package declarations again dictate the final locations of the .class files.

Importing

Until here, we talked about compiling and finding the right class in the right place while running the programs. Here, we will try to understand what is needed and what happens when we access a class inside another package. Almost all classes Java developers use are contained within many different packages. There is only one exception to this, which is the java.lang package. It is so vital for Java that, it is literally impossible to do anything without it. Henceforth, it is imported implicitly for all Java classes. Other than those, every class accessed must be referred correctly. To do that, we must be sure of two things:

  • The referred class must exist under the class path, with its proper folder structure according to its package name
  • In the source code, that class must be explicitly imported

So we came to the point of importing classes. In Java, the first non-comment line of the source codes contains the package name. After that, the import expressions must be written. Either, we can make a single-type-import for that class/interface, or all classes/interfaces under a specific package. The “all” part is defined by an asterisk (). java.util. means all classes/interfaces of the java.util.package. There are two important points here. The first one is that, by this way, only classes/interfaces can be imported. The other one is, the child packages of java.util ARE NOT imported. They must be explicitly imported. In this case, if we want to use Semaphore class in java.util.concurrent package, we also need to import that class or all classes/interfaces inside java.util.concurrent. A List of Semaphores can be used as in below:

import java.util.List; //import java.util.* also fine
import java.util.concurrent.Semaphore; // java.util.concurrent.* also fine

public class ConcurrencyCollection {
  private List<Semaphore> listOfSemaphores;
}

The rule is: When a class/interface is referenced inside a Java code, its package name is prefixed and searched for in the class path. So, according to the above statements, when we refer to a List object, the compiler searches for java.util.List in the class path. This means that, we can also achieve the same result without using imports, but explicitly using fully-qualified names of the classes. The following code is perfectly valid.

public class ConcurrencyCollection {
  private java.util.List<java.util.concurrent.Semaphore> listOfSemaphores;
}

It is cumbersome to write the fully-qualified names of each class/interface, therefore import mechanism is used extensively. However, this representation is very important and can be a life saver in some situations. For example, let’s assume that we need to import all classes/interfaces in java.util and java.awt packages. We would like to have a List of Semaphores again.

import java.util.*;
import java.util.concurrent.*;
import java.awt.*;

public class ConcurrencyCollection {
  private List<Semaphore> listOfSemaphores;
}

ambiguity

Since List class/interface happens to be in both java.util and java.awt packages, compiler cannot decide which one we are trying to use. In this very specific case, we must use the fully-qualified name of the List interface. The small modification below makes everything ok again.

import java.util.*;
import java.util.concurrent.*;
import java.awt.*;

public class ConcurrencyCollection {
  private java.util.List<Semaphore> listOfSemaphores;
}

If the only class/interface we are interested in java.util package is the List, then we have another option.

import java.util.List;
import java.util.concurrent.*;
import java.awt.*;

public class ConcurrencyCollection {
  private List<Semaphore> listOfSemaphores;
}

Wow, it just compiled smoothly! The magic here is that, single-type-imports override wildcards. Throughout the source code file, the List will be assumed to be java.util.List. Therefore, single-type-imports for classes/interfaces can be a better choice when dealing with many imports. Of course, when the classes/interfaces having the same name are imported in a single-type fashion, the compiler refuses to proceed successfully, because of a collision. 

import java.util.List;
import java.util.concurrent.*;
import java.awt.List;

public class ConcurrencyCollection {
  private List<Semaphore> listOfSemaphores;
}
collision

Static Import

I said that the import mechanism only works for classes and interfaces but, frankly, it is not totally true. Starting with Java 5, a new way to import public static fields and methods was introduced. That is static import. The aim of it is to eliminate Constant Interface Anti-pattern. Software developers devised this anti-pattern to share the static fields in their many classes without a qualification. There are many frequently used constants, such as Math.PI, Integer.MAX_VALUE, Long.MIN_VALUE, e.g. To bypass their corresponding class names, an interface can be created and these values can be put into it as the only API. Abusing the interface mechanism can yield to significant problems. Instead, we can create single utility classes, put those public static fields as final. Then, use them by statically importing, which practically achieves the same result with lesser side-effects. Although it is a valid option, usage of it is generally discouraged.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s


%d bloggers like this: