Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Grouping With Packages

Key Concepts

It can be useful, particularly in larger systems, to have distinct logical groupings of classes1. These groups of classes are called packages.

The classes within a package all ‘fit together’ in some sense; typically, they are used together to implement a particular feature of the system, or they form a distinct architectural component of the system.

For example, you might have an application with one group of classes that are used to provide a user interface, and another group of classes that handle interaction with a database. You could put the first group of classes in a package named myapp.ui and the second group in a package named myapp.database.

Or you might organize things functionally, putting all the classes that relate to placing an order for a product in the myapp.orders package, and all the classes that relate to delivering the ordered product in the myapp.delivery package.

A package constitutes a namespace for your code. Within that namespace, class names need to be unique, but the same constraint doesn’t apply across namespaces.

This can be useful in large projects, where different teams are working on different parts of a system. It may well happen that two teams happen to use the same name for different purposes. This would cause problems if all the code were part of a single namespace, but if the teams put their classes in differently-named packages then the problem disappears.

An Example From Java

The Java language ships with a huge standard library comprising thousands of classes. These are organized into a large number of packages.

The package java.util contains a class named Date, which represents a date & time with millisecond precision. Another package, java.sql, contains a different class that is also named Date, representing a date retrieved from an SQL database.

These two classes are allowed to have different names because they reside in differently-named packages. If you ever needed to reference both classes from within the same file of source code, you could use the package names to distinguish between the two.

Packages in UML

A package is represented in UML as a file folder, with a tab sticking out of one corner. If you are not interested in seeing the contents of a package, the package name can be displayed in the middle of the folder icon:

For more detail, you can put the package name in the tab and show the package contents inside the folder, either as a full class diagram or as a list of class names:

Packages in Kotlin

Putting Code in a Package

This is fairly straightforward. If you put a package declaration at the top of a .kt file, all of the classes and functions defined within that file will become part of the package that you have named in the declaration.

For example, to put class Customer in the package myapp.orders, you would write

package myapp.orders

class Customer(val name: String, val email: String)

Package Naming Conventions

Package names generally have two or more elements, separated from each other by a period. Each element should use lowercase letters if possible, although a lower camel case naming style is permitted. Underscores should not be used.

In code intended for public release, it is advisable to make package names unique by incorporating a reversed domain name as a prefix.

Consider, for example, the Kotest libraries that you have already used for unit testing. The website for Kotest is hosted at the domain kotest.io, and all of the code in the libraries is organized into packages whose names begin with io.kotest.

Implications For Project Organization

Package names affect where the output from the Kotlin compiler is stored.

Let’s experiment with this now.

  1. Examine the file Customer.kt, in the tasks/task14_1_1 subdirectory of your repository. This file doesn’t have an explicit package declaration. The class defined in the file will be treated as if it were part of an unnamed ‘default package’.

  2. In a terminal window, compile the code using the command-line compiler:

    kotlinc Customer.kt
    

    Verify that this has created a bytecode file named Customer.class, in the same directory as Customer.kt. Then remove this bytecode file.

  3. Edit Customer.kt and add this line to the start of the file:

    package myapp.orders
    

    Invoke the compiler again. Where is Customer.class now?

Note that it is customary to organize source code in a directory hierarchy that mirrors the package name.

So if you put a class into a package named myapp.orders, the .kt file containing the class definition should ideally be located in the subdirectory src/myapp/orders.

The Kotlin compiler doesn’t require this, but it is considered to be good practice.

Warning

You’ll have noticed that packages generally haven’t been used in the tasks and code samples that we’ve provided for you.

This has been done to keep things simple, and because the amount of code used in a task is generally very small, so there is not much benefit to be gained by putting functions and classes into packages.

In larger pieces of work—e.g., your Semester 2 group project—we will expect you to use packages or similar mechanisms to keep all of your code well organized.


  1. We focus on classes here, but this idea of grouping applies equally to functions.