Chapter:Classes - Using packages

From Juneday education
Jump to: navigation, search

Meta information about this chapter

Expand using link to the right to see the full content.

Introduction

This lecture revisits the topic of packages from a more practical point of view. Earlier, we learned about the package statement and its relation to where to store the source code file of a class in a package. Now, we try to motivate the use of packages and why to learn about them and use them.

Purpose

The purpose of this lecture is to remind and motivate the student to learn about and use packages, in order to better understand APIs and how they are organized, as well as to make it an habit to organize their classes in packages.

Goal

After this chapter, the student shall understand and know:

  • Why packages are a good idea
  • Various ways to group classes into packages (logically or otherwise thematically)
  • How to use packages as namespaces to manage name collisions
  • That packages are used in "real" APIs and applications

Instructions to the teacher

Common problems

Students sometimes struggle to compile and run applications in packages from the command line. A classic mistake is to cd down to the same directory as the source code file they want to compile or as the class file they want to run. Explain to students that there is no need to leave the top level directory above the first part of the package. Also show the students how the destination flag (-d) to javac works, when you work with a directory layout with e.g. an src directory and a bin directory to separate source code from class files. In conjunction to this, show the students how the classpath flag (-cp) to the java command works (e.g. allowing you to run files in packages placed under the bin directory).

If the students run into typical class not found errors etc, take the opportunity to discuss with them how both the compiler and the java command resolves class names. This will re-enforce the connection between packages and relative paths. You can ask questions like "If you want to compile the file x/y/Z.java, what directory do you have to be in?" The answer is of course "the directory above x". Next, ask what the argument to javac would be "if you have cd:d down to y". The answer is of cource "Z.java". Now, ask what would happen if the Z class has an import statement, importing x.foo.Y when you are in the y directory. The answer is that the compiler would look for the x.foo.Y source code, and it would require that there is a directory called x realative to the directory you are currently in. Now, that won't work, since you have parked yourself in y!

.
`-- x
    |-- foo
    |   `-- Y.java
    `-- y
        `-- Z.java

In order for the compiler to find and compile the class source code for x.foo.Y imported in x.y.Z, the compiler looks for the relative path x/foo/Y.java . If you are in "." above x, then compiling x/y/Z.java will also compile the imported class, because the compiler will translate the class name x.foo.Y to the relative path x/foo/Y.java which is a valid path from "." above x. But if you cd down to the y directory, the relative path x/foo/Y.java won't be valid, since there isn't even a x directory in y.

The best advice to the students is to never leave "." so that both relative paths are valid. Standing in "." above x allows the student to create the files x/foo/Y.java by giving exactly that relative path as argument to her editor. Same with x/y/Z.java . Without leaving "." ! Also, still in "." the student can compile both files using the same relative paths. There is no need to go down in the directories below the current directory.

Using a layout with src and bin for separation of source code and class files, makes things slightly more complex. The advice is similar, though. Don't leave the directory where you have both src and bin. You can create the source code files using relative paths all the way to the source code files. But compiling a file below src (which is not part of a package name), will only work as long as the class doesn't import anything from a sibling package. The solution is to use the class path flag.

.
`-- src
    `-- x
        |-- foo
        |   `-- Y.java
        `-- y
            `-- Z.java

Let's again assume that Z imports x.foo.Y . Compiling src/x/y/Z.java requires the classpath to indicate the directory where the top level package is, i.e. where x is, which is src. The command line then becomes: javac -cp src src/x/y/Z.java . You can explain this to the students by saying that the class path src means "by the way, packages are located in src/".

What happens is that the file src/x/y/Z.java is compiled without problem, since javac finds the file in that relative path. But when it finds the import statement import x.foo.Y; it looks at the class path and finds "src". Then, javac looks for the directory "x" under "src/". Simple as that. But, expect this to take some time and practice to sink in.

Classes: Using Java packages

Just as we like to organize our files on our computer in a hierarchical structure using directories, we want to maintain such order in our Java programs as well. Programmers tend to organize the classes in the system in directories with a hierarchical structure with descriptive names of the directories. This has many benefits. One is that the classes get a "name space" using the package names as part of the full class name. This, in turn, allows us to have two or more classes in our system with the same name, if they belong to different packages.

The package name corresponds to the relative path to the class file (and source code file). This allows us not only to have descriptive names of classes where the package names are part of the full (qualified) class name, but also to have the same name of the actual class, since the class files are kept in separate directories:

org.somecompany.net.facebook.LoginCredentials;
org.somecompany.net.instagram.LoginCredentials;
org.somecompany.net.google.LoginCredentials;

All the classes declared above are declared with the name LoginCredentials, but the qualified names show us the context. Just by looking at the full, qualified, names of the classes we understand that they are representations of the login credentials for different social network services.

Another benefit from using packages is that we can group classes together according to some criteria. For instance, we could have all the classes dealing with networking in a directory (and package!) and all classes which deal with the graphical user interface in another directory (and package!). This way, it is easy for the programmers working on the source code to find the source code files. If a programmer has to fix a problem with the internet connectivity of the system, she would be able to look at the directory names and figure out where the relevant source code files for the classes probably are located: "They are probably somewhere under org/somecompany/net...". And when another programmer needs to work on the graphical interface of the system, she would similarly probably think: "The classes for this are probably somewhere under org/somecompany/gui/..." (gui often stands for "Graphical User Interface").

The perhaps most striking reason for using packages (and the corresponding directory structure) is that the alternative would mean that we keep all source code files (and after compilation, all class files) in one big directory. For a large project with thousands of files, this would of course be a great mess.

Finally, a less obvious benefit for using packages, is that we could actually use the "default" or "package private" access level for variables and methods. This access level is what we get when we don't use any of "public", "private" or "protected". The absence of an access modifier keyword gives us this level which is called "package private" (or simply "default"). Such variables and methods are accessible within the class itself and from classes in the same package.

It is not uncommon that we would give some methods visibility only in related classes, so that those classes can use the method but no other classes in the system. Related classes, as we said above, should be put in the same directory (and package), so the default access level works fine in such a case. Let's go back to the SavingsAccount example. Perhaps we have a class responsible for changing the interest rate of the different bank account classes. If we put this class in the same package as the classes for the accounts, we could give the static method changeInterestRate(double) default access level. The benefit of this approach could be that we effectively prevent "unauthorized" classes (unrelated classes) to mess with the interest rate values of the different bank account classes. We could then centralize the security measures for deciding if an interest rate could be changed to the class which has access to the various changeInterestRate(double) methods of the respective classes.

But from a foreign (unrelated) class, like org.somecompany.main.Main, the various changeInterestRate methods are not accessible, since this class belongs to a different package.

Chapters about classes

Here are the chapters about classes in this book, so that you can keep a check on your progress:

Videos

Links

Further reading

Where to go next

Next page has exercises for this chapter: Classes_-_Using_packages_-_Exercises

« PreviousBook TOCNext »