This course has already ended.

Java packages

The section describing the Java code file structure mentioned that a code file can begin with a package declaration of form “package package_name;”. A package declaration states that all classes defined in the code belong to the package named in the package declaration. Consider the simple example code:

package fi.tuni.programming3;

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
}

Here the class HelloWorld was declared to belong into the package fi.tuni.programming3. This in effect means that the class HelloWorld has a so-called “fully qualified” name fi.tuni.programming3.HelloWorld. A package declaration defines the class into a namespace. This is on a high level similar to, e.g. C++ namespaces. References to classes (or interfaces) of a package use a similar dot notation as class member references. For example fi.tuni.programming3.HelloWorld refers to the class HelloWorld in the package fi.tuni.programming3, and on the other hand fi.tuni.programming3.HelloWorld.main refers to the main member function of that class.

The package name “fi.tuni.programming3” used above was an example of the widely adopted convention that package names are based on the internet domain name of the code owner. A package name lists the domain name parts in reverse order. E.g. here the Tampere university domain name “tuni.fi” formed the basis of the package name, and the suffix programming3 would further specify that the package is related to this course. A real life example could be, e.g. the fact that Java libraries published by Google and Microsoft are defined in packages whose names start with com.google and com.microsoft, respectively.

Packages help avoid class name clashes. For example, the Java class library contains two different classes named Date: one in the package java.sql and another in the package java.util. These classes could not be used in the same program without packages, because in that case the Java compiler would not know which class the name Date refers to. We can use fully qualified names, that is, names prefixed with package names, to refer to classes, and this allows to distinguish the two Date classes by referring to them as java.sql.Date and java.util.Date. It should be obvious by now that a package should not contain two classes with the same name, as then even the fully qualified names would be unable to distinguish between them.

Packages vs import

We stated before that import statements can be used to make external classes accessible in the current code file. To be more precise, the import statements allow us to use a simple (not fully-qualified) class name when referring to a class defined in some other package. An import is not needed if we use a fully qualified class name. For example the following two code snippets that use the sort function of the class Arrays from the package java.util are in effect identical:

import java.util.Arrays;

public class SortedParameters {
  public static void main(String[] args) {
    Arrays.sort(args);
    for(String arg : args) {
      System.out.println(arg);
    }
  }
}
public class SortedParameters {
  public static void main(String[] args) {
    java.util.Arrays.sort(args);
    for(String arg : args) {
      System.out.println(arg);
    }
  }
}

As mentioned above, import statements concern classes defined in different packages. A code file can refer to all classes of the same package with simple class names without using import statements.

A second motive for using packages is that they facilitate grouping related classes together. This is useful especially in large software projects that may contain hundreds or even thousands of classes. Using packages in Java actually more or less imposes a hierarchical directory structure on the code base: Java interprets a package name as a directory path that specifies where the code files are stored. Dot characters of a package are interpreted as separate directory levels. For example the class HelloWorld defined in the package fi.tuni.programming3 should be stored in the subdirectory “fi/tuni/programming3” (or “fi\tuni\programming3” in Windows). A class defined in a package should be executed using the fully qualified class name, for example as java fi.tuni.programming3.HelloWorld. Here the Java virtual machine would begin the program execution from the class file fi/tuni/programming3/HelloWorld.class (or fi\tuni\programming3\HelloWorld.class in Windows).

If we follow the principle that package names begin with reversed internet domain names, the code base will be organized into a directory structure where, e.g. all code from the same organization will be located under the same subdirectory.

Packages and Maven

It’s possible (and recommended) to use packages in Maven projects. Just like in the example class, the code files begin with a package statement, which was package fi.tuni.programming3; in our HelloWorld example. The source files are also arranged in a directory structure corresponding to their packages. Therefore the HelloWorld class would be placed in the path src/main/java/fi/tuni/programming3/HelloWorld.java.

Packages require some changes to the project file pom.xml too. Here’s an example of a project file that could be used for a Maven project made for the HelloWorld example class.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>fi.tuni</groupId>
  <artifactId>programming3</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>com.jolira</groupId>
        <artifactId>onejar-maven-plugin</artifactId>
        <version>1.4.4</version>
        <executions>
          <execution>
            <configuration>
              <mainClass>fi.tuni.programming3.HelloWorld</mainClass>
              <onejarVersion>0.97</onejarVersion>
              <attachToBuild>true</attachToBuild>
            </configuration>
            <goals>
              <goal>one-jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

In Maven projects it is customary that the groupId and the artifactId together form the Java package of the project. For example, if the goal is to place the source files of the project in the package fi.tuni.programming3, the groupId would be fi.tuni and the artifactId programming3. This is not enforced by Maven; however, experienced developers using Maven expect this rule to be followed, so you should follow it too, even if the values technically could be different.

Note also that the value of the mainClass element has to include the fully qualified package of the main class. In this example, since the main class HelloWorld is in the package fi.tuni.programming3, the value of the mainClass element needs to be fi.tuni.programming3.HelloWorld.

In this week’s exercises and in those of subsequent weeks your source code must be attached to the stipulated package in order for the automatic grading to successfully grade your work.

Posting submission...