04: Namespaces, Packages and the CLASSPATH; JUnit
Announcements
Assignments: I know that for many of you the first assignment was pretty easy, but for many of you it was not. As of about noon today, only 60 (out of the 86 enrolled) had submitted A01. I strongly suggest you start earlier. This course is gentler than 187, but that does not mean you should wait until the day things are due to start them. As assignments get harder, you’re going to want/need to start them, get stuck, ask questions, think about them, get going again, etc., which takes time. You won’t have that time if you don’t start until they’re due.
Piazza: You should not post your code in public posts! Doing so is a violation of the Academic Honesty Policy! Use private posts instead.
Again: If you’re a late add, you must add yourself to Piazza and Gradescope, and ask us to be excused from whatever you missed. SPIRE does not notify me about late adds. If you used SPIRE to add yourself and not done anything else, you’re gonna start seeing zeros for grades soon.
Namespaces
Many programming languages, including Java incorporate the idea of a “namespace”. A namespace is a way to provide context to a particular name.
For a real-world analogy, you might think of a person’s name, say, “Nicholas”. There might be more than one in this class, so we add some context (a surname, or a student ID, or an address, or all of the above) to disambiguate which we mean.
This is very similar to the idea of a variable’s scope in Java, but slightly different, as it’s how we precisely name and identify classes.
For example, we all write System.out.println()
all the time, and we all know that System is probably a class, since it starts with a capital letter. Where does it come from, though?
Packages
It’s part of the java.lang
package. Java organizes classes into packages; which are a hierarchical sequence of tokens (words), separated by dots. The built-in parts of the Java standard library all are part of the “java.” package, though it’s further subdivided.
For example, the aforementioned java.lang
package defines the classes that are fundamental to the design of the language itself: things like System
and String
are defined here.
http://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html#package.description
By default, things in this namespace are automatically “imported” into the local namespace. That is, you don’t have to type java.lang.String
to declare a String
(though you can); String
suffices.
Interestingly, it’s not against the rules to define your own System
class. But it’s like if you have both an instance variable named x
and a local variable named x
. By default, Java assumes you want to “more local” one.
int x = 5;
void test() {
x = 3;
System.out.println(x);
System.out.println(this.x);
}
To get the “outer” one, you need to prefix it with this.
, which tells Java you want the current instance variable with the same name.
Exercise:
private String x = new String("Jane");
public void printStrings() {
String x = new String("Ren");
System.out.println(x);
System.out.println(this.x);
}
What are the lines output by printStrings()
?
Another exercise:
private String x = new String("Jane");
public void printEquals() {
String x = new String("Jane");
System.out.println(x == this.x);
System.out.println(x.equals(this.x));
}
What are the lines output by printEquals()
?
a. true; true b. true; false c. false; true d. false; false
Back to lecture.
Similarly, with a class, you need to fully specify the class if you want access to it. Inside your custom System
class, if you want to access the “normal” System.out.println
, you’ll need to refer to it by its full name, java.lang.System.out.println
.
java
and javax
are reserved by the JVM for built-in and extensions classes, but much like anyone can register a domain name on the Internet, anyone can declare a package namespace. There’s a loose convention that you should use your reverse domain name as a prefix (for example, if our department released a package for autograding, we might put it in the edu.umass.cs.autograder
package). But many modern java packages declare a top-level namespace – you’ll see this in Lab 3, where the Processing project just prefixes their namespace with “processing”. In practice, projects that do similar things don’t usually have the same name, and/or agree to avoid namespace collisions.
Importing packages
Sometimes you want to use something not in the current namespace. Then you need to “import” it.
For example, if I want to print a random number between one and six:
public class Die {
public static void main(String[] args) {
Random r = new Random();
System.out.println(r.nextInt(6) + 1);
}
}
it won’t compile, because Random
is not in the namespace. But I can use an import statement: import java.util.Random;
to add it to the namespace.
Eclipse will do this for you in one of its “quick fixes” but beware: there’s sometimes more than one class with the same name! If you import the wrong one, it likely won’t have the behavior you expect!
Finding packages
Where does Java look for packages? By default the JVM has access to a set of “built-in” packages, that form the Java Platform API:
https://docs.oracle.com/javase/8/docs/api/index.html?overview-summary.html
Again, mostly in the java
and javax
namespace, but also some others.
But where do the compiled classes, that is, the virtual machine code for them, actually live? On my machine, a big chunk of them live in /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre/lib
in the JARs there.
JARs are essentially ZIPfiles of compiled Java classes with some extra stuff (a Java-specific manifest, describing their contents, that the JVM knows how to read). Let’s take a look in rt.jar
. Hey look! Our friends System
and String
!
You may have noticed that the file, say String.class
(which is the compiled representation of String.java
) lives in a directory java/lang/
. That looks a lot like java.lang.
, doesn’t it?
Not a coincidence! The JVM requires that packages map to (that is, directly correspond to) directories with the same name(s), and that classes map to .class
files within those directories.
But there’s still a piece of the puzzle missing. How does the JVM know where to look for these directories? How did it know, for example, that /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre/lib/rt.jar
was a place to search?
CLASSPATH and friends
There are three mechanisms the JVM uses. One is under your control: the “CLASSPATH”. The other two you can’t (easily) change – there is a “bootstrap CLASSPATH” and an “extensions directory”.
The latter two are configured when your JVM is installed, and they contain classes that are part of the JRE, JDK platform, and vendor-distributed extensions.
But the CLASSPATH you do control. If you run from within Eclipse, you can add JARs (and directories) to the CLASSPATH by selecting the appropriate menu items, either “Build Path -> Add to Build Path” for JARs or “Build Path -> Use as Source Folder” for a directory. You can also manage the Build Path (“Configure Build Path”) and view what’s on it, which is how I found the rt.jar
I showed you earlier.
Putting your own classes into packages
If you want to put a class into a package, like, for example, you want to move our Die
class into the nerdy.gaming
package, you need to explicitly declare the package at the top of the file, and move it into an appropriate directory, in order for it to compile and be recognized by the JVM. Eclipse will prompt you to do one if you do the other.
Notice that now the top of the file has a nerdy.gaming
package declaration; it lists just the package, not the classname (unlike import
s, which list a full class name). Also notice that the package is rooted at a directory that’s in our CLASSPATH; in this case, the src/
directory in the Eclipse project. You can tell it’s in the CLASSPATH due to the little “target” on the folder; it’s on the so-called “build path” which Eclipse uses as one component of the CLASSPATH.
Class exercise
(first, on board:) Suppose I had the following directory hierarchy:
src/marc/liberatore/Banana.java
support/marc/liberatore/Smoothie.java
and the following code in Smoothie.java
:
class Smoothie {
public static void main(String[] args) {
System.out.println("Add a " + new Banana());
}
}
and I intended the two classes to both live in the marc.liberatore
package.
Questions:
- What directories need to be on the classpath in order for this to compile?
- What
package
should we declare at the top of this file? - Do we need to
import
anything? If so, what?
A brief tour of Eclipse and unit tests
Let’s create a simple calculator class and some tests:
package scratchpad;
public class Calculator {
public int add(int x, int y) {
return x + y;
}
public static void main(String[] args) {
Calculator c = new Calculator();
System.out.println(c.add(1, 2));
}
}
Here, we are manually testing. Let’s automate it.
We can right-click in the package manager to add a test class (though for most projects they’ll already be provided, and you can just edit them).
package scratchpad;
import static org.junit.Assert.*;
import org.junit.Test;
public class CalculatorTest {
@Test
public void test() {
fail("Not yet implemented");
}
}
Notice that it imports the Test
class, as well as several static methods from the org.junit.Assert
class for you. This is the source of the assertEquals
and whatnot. There are many to pick from: http://junit.sourceforge.net/javadoc/org/junit/Assert.html though generally you assert equality between an expected (known by a human to be correct) value and the value your code produces (the “actual” though perhaps incorrect value).
So let’s add test to replace our manual test for add
, and remove the dummy test method:
@Test
public void testAddPositive() {
Calculator c = new Calculator();
assertEquals(3, c.add(1, 2));
}
This is a good general pattern to work from. Imagine you have a driver class with a main method; instead of coding your tests as a series of println
, capture that knowledge into a set of JUnit tests.
Note that we need to annotate each test with the @Test
decoration; this tells Eclipse (and other tools) that the following method is a unit test to run. If you forget the decoration, the test won’t be run. While we’re here, let’s add a test for an as-yet-unwritten subtract method:
@Test
public void testSubtractPositive() {
Calculator c = new Calculator();
assertEquals(1, c.subtract(3, 1));
}
And then let’s implement subtract
:
public int subtract(int x, int y) {
return x + y;
}
If we run the tests … boom! Typo. Fix it, and the tests pass.
FYI, sometimes assertions have an extra first argument, which is text that the test runner will print out (you’ll see this in some projects).