For a package to be well-formed, the package's parent directory must exist in your CLASSPATH environment variable -- for 6.170 this is already taken care of because our recommended modifications modify $CLASSPATH to include /mit/$USER/6.170 by default. This is why your problem sets must be in directories named psN under your 6.170 directory.
Additionally, each file in the package must have a declaration of the form:
package psN;
which allows a class to know its own namespace, among other things.
For example, in your Problem Set 2 class GeoSegment, you
might have made a reference to the class GeoPoint. The compiler
has a list of packages you've imported via the import construct
(let's say java.util),
as well as the class's own package (ps2). It looks in each
of these packages for the GeoPoint class until it finds it, or
issues an error.
Thus, you will get a cannot resolve symbol error from javac if you all of your classes for a particular project are not consistently declared as part of the same package. Some permutations of this error are shown in the exercises. As a quick example of this error:
Foo.java:5: cannot resolve symbol
symbol : class Bar
location: class package1.Foo
private Bar barbarbar;
^
The error message tells you that the compiler believes it's compiling a class "Foo" in package "package1". For some reason, it's encountered a naked class without a package, most likely because there's no class "Bar" in the same package so it can't walk through the search order as described above.
Another common cause of this error is by misspelling a variable or class or field name:
OptimizedLinkedList.java:9: cannot resolve symbol
symbol : variable theTal
location: class OptimizedLinkedList
theTal = null;
^
In this case the intended field is called theTail.
You'll also see this error if you have code which tries to reference a variable that was declared in another scope, like:
...
try {
double ran = Math.random();
if (ran > .5) {
throw new Exception("random number too large");
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
double result = ran; // <<<<<< "ran" is out of scope here
System.out.println("Random number is " + result);
...
The variable ran is only valid inside the try block, and so it becomes an unresolved symbol if you try to access it outside the block (unless you also have another variable called ran in the outer scope).
An interesting side-effect of the type-genericity of Java 2's Collections classes is that the return type for all producers is Object, since that's the lowest-common-denominator class of anything that can be stored in a Collection.
Therefore, from the compiler's point of view, Vector.get() always returns an object, regardless of the fact that you, the programmer, know that at run-time the Vector will only contain Integers, for instance. If you have a piece of code like:
...
Vector vec = new Vector();
vec.add(new Integer(5));
int a = vec.get(0).intValue();
...
it will yield the compile-time error:
Foo.java:13: cannot resolve symbol
symbol : method intValue ()
location: class java.lang.Object
int a = vec.get(0).intValue();
^
The unresolved symbol error stems from the fact that the compiler only knows that vec.get(0) returns an element of compile-time type Object, which does not have method intValue() defined for it. The tip-off is that the "location:" is showing java.lang.Object instead of java.lang.Integer. You need to tell the compiler what the run-time type will be so it can find the correct method.
Note that you must be certain that what you are casting will actually have that type at run-time. If it does not, you'll have a ClassCastException. Inversely, if you have a ClassCastException at a particular line, you can debug it quickly by adding debugging code like:
...
Object testclass = vec.get(0);
System.err.println("At line 13 of Foo.java: " + testclass.getClass().getName() +
": " + testclass);
int a = ((Integer)vec.get(0)).intValue(); // <<<<<< ClassCastException happens here sometimes
...
This will print out the Vector elements' classes and use their toString() method
(which is dispatched at run-time to the appropriate method in the run-time class).
JUnit provides a good testing harness for step 1 (finding a test case that exhibits the error), provided you have good test cases. Here are some tools to help you debug.
During debugging, it may be helpful to to use a toString method that prints every field in your class, potentially with a field-name label. Since this may expose your internal variables to clients, you should disable the verbosity of the string for final versions of your code.
For example, your GeoSegment class from Problem Set 2 might output something like:
GeoSegment: Memorial Dr. p1: GeoPoint: (42358333, -71060278) p2: GeoPoint: (42358333, -71079857)
Note that in this case, it would be most modular if GeoPoint had it's own toString method that is called to generate "GeoPoint: ..." in the last two lines.
Remember that in the following three cases:
Object bat = getNextObjectOfRandomClass();
System.out.print("bat: "); System.out.println(bat);
System.out.println("bat: " + bat);
System.out.println("bat: " + bat.toString());
an equivalent string is printed out. The middle case is usually most
convenient.
Entering method doSomething(Vector a, Object b, Word c) a: [ "lala", "looloo" ] b: null c: Word: word: "nothing"In this case we can clearly see that parameter "b" is null on this particular invocation, and that "a" only holds 2 elements.
The entire method signature is included in the output to isolate which particular method is being called, since it can be ambiguous with Java's method overloading, where multiple methods can share the same name but take different parameters
public class MyException extends Exception {
public MyException() {
super();
}
public MyException(String s) {
super(s);
}
}
Unchecked exceptions only differ in that you extend RuntimeException
A combination of the two techniques above can be applied when you get a back trace. Although you can tell the location of the error, you might not immediately be able to tell the conditions under which the error is occuring. Before each line in your trace, do a variable dump of what's being passed as parameters to the method in the trace. Note: doing this in addition to method entry dumps will give you the same information twice, which might be too verbose.
This technique is more useful when your code mostly works, and you're trying to debug a specific exception, unlike if you need method entry checks to look at method invocation in every part of your code.
jdb isn't discussed in detail here, but it is an alternative tool that can help you. More information about jdb can be found at Sun's site.
Inserting these calls at the beginning and end of public methods can make your code robust, since it will be far less likely that your internal state will become invalid without you realizing it.
athena% mkdir ~/6.170/lab5
athena% cp -r /mit/6.170/www/labs/lab5/assignment/* ~/6.170/lab5/
athena% cd ~/6.170/lab5
Reproduce the follow cannot resolve symbol errors
using the provided files Class1.java and Class2.java
which should be placed in ~/6.170/lab5/symbol/. We will
use the package lab5.symbol, which works equivalently
to a top-level package as described earlier, except it's one
level nested.
Class1.java:5: cannot access Class2
bad class file: ./Class2.class
class file contains wrong class: lab5.symbol.Class2
Please remove or make sure it appears in the correct
subdirectory of the classpath.
private Class2 c2;
^
|
Class1.java:5: cannot resolve symbol
symbol : class Class2
location: class lab5.symbols.Class1
private Class2 c2;
^
|
Class1.java:8: cannot resolve symbol
symbol : constructor Class2 ()
location: class lab5.symbol.Class2
c2 = new Class2();
^
|
./Class2.java:8: cannot resolve symbol
symbol : constructor Class1 ()
location: class lab5.symbol.Class1
c1 = new Class1();
^
|
To reproduce these errors, you'll need to modify one or both of the source files, and run:
athena% javac Class1.java
in your ~/6.170/lab5/symbol directory. If you get other
errors, ignore them as long as you can reproduce the stated error.
The Javadoc for these classes can be found here.
athena% javac *.java
athena% java lab5.DictionaryLoader
Read through the logs during this invocation. The DictionaryLoader
assembles a dictionary, using the DictionaryEntry
class you modified. If you want to store this output, you can do:
athena% java lab5.DictionaryLoader > dictionary.out
athena% emacs dictionary.out
There are at least 3 suspicious entries that you should identify.
These examples are fairly contrived, since they clearly violate the requires clause. In defensive coding, you would make a check for these violations explicitly. However, dumping out values can help in determining coverage and execution paths, to better understand the average usage of your code.