What do "Cannot find symbol," "Cannot resolve symbol," or "Symbol not found" errors mean in Java, and how can you resolve them?

What do “Cannot find symbol,” “Cannot resolve symbol,” or “Symbol not found” errors mean in Java, and how can you resolve them?

In my opinion, understanding common error messages can greatly improve your debugging skills.

Is there any difference between these errors?

Not really. “Cannot find symbol”, “Cannot resolve symbol” and “Symbol not found” all mean the same thing. (Different Java compilers are written by different people, and different people use different phraseology to say the same thing.)

  1. What does a “Cannot find symbol” error mean? Firstly, it is a compilation error. It means that either there is a problem in your Java source code, or there is a problem in the way that you are compiling it.

Your Java source code consists of the following things:

  • Keywords: like class, while, and so on.
  • Literals: like true, false, 42, ‘X’ and “Hi mum!”.
  • Operators and other non-alphanumeric tokens: like +, =, {, and so on.
  • Identifiers: like Reader, i, toString, processEquibalancedElephants, and so on.
  • Comments and whitespace.

A “Cannot find symbol” error is about the identifiers. When your code is compiled, the compiler needs to work out what each and every identifier in your code means.

A “Cannot find symbol” error means that the compiler cannot do this. Your code appears to be referring to something that the compiler doesn’t understand.

  1. What can cause a “Cannot find symbol” error? As a first order, there is only one cause. The compiler looked in all of the places where the identifier should be defined, and it couldn’t find the definition. This could be caused by a number of things. The common ones are as follows:

For identifiers in general:

  • Perhaps you spelled the name incorrectly; i.e., StringBiulder instead of StringBuilder. Java cannot and will not attempt to compensate for bad spelling or typing errors.
  • Perhaps you got the case wrong; i.e. stringBuilder instead of StringBuilder. All Java identifiers are case sensitive.
  • Perhaps you used underscores inappropriately; i.e., mystring and my_string are different. (If you stick to the Java style rules, you will be largely protected from this mistake…)
  • Perhaps you are trying to use something that was declared “somewhere else”; i.e. in a different context to where you have implicitly told the compiler to look. (A different class? A different scope? A different package? A different code-base?)

For identifiers that should refer to variables:

  • Perhaps you forgot to declare the variable.
  • Perhaps the variable declaration is out of scope at the point you tried to use it. (See example below)

For identifiers that should be method or field names:

  • Perhaps you are trying to refer to an inherited method or field that wasn’t declared in the parent / ancestor classes or interfaces.
  • Perhaps you are trying to refer to a method or field that does not exist (i.e., has not been declared) in the type you are using; e.g., “rope”.push().
  • Perhaps you are trying to use a method as a field, or vice versa; e.g. “rope”.length or someArray.length().
  • Perhaps you are mistakenly operating on an array rather than array element; e.g.,
String strings[] = ...
if (strings.charAt(3)) { ... }
// Maybe that should be 'strings[0].charAt(3)'

For identifiers that should be class names:

  • Perhaps you forgot to import the class.
  • Perhaps you used “star” imports, but the class isn’t defined in any of the packages that you imported.
  • Perhaps you forgot a new as in:
String s = String();  // Should be 'new String()'
  • Perhaps you are trying to import or otherwise use a class that has been declared in the default package; i.e., the one where classes with no package statements go.

Hint: learn about packages. You should only use the default package for simple applications that consist of one class … or at a stretch, one Java source file.

For cases where type or instance doesn’t appear to have the member (e.g., method or field) you were expecting it to have:

  • Perhaps you have declared a nested class or a generic parameter that shadows the type you were meaning to use.
  • Perhaps you are shadowing a static or instance variable.
  • Perhaps you imported the wrong type; e.g., due to IDE completion or auto-correction may have suggested java.awt.List rather than java.util.List.
  • Perhaps you are using (compiling against) the wrong version of an API.
  • Perhaps you forgot to cast your object to an appropriate subclass.
  • Perhaps you have declared the variable’s type to be a supertype of the one with the member you are looking for.

The problem is often a combination of the above. For example, maybe you “star” imported java.io.* and then tried to use the Files class … which is in java.nio not java.io. Or maybe you meant to write File … which is a class in java.io.

If you forget the new keyword when creating a new instance of an object in Java, you might encounter an error. For example, consider the difference between these two lines of code:

String s = String(); // Incorrect
String s = new String(); // Correct

In the first line, String() is treated as a method call, attempting to find a method named String with no arguments, which is not a defined method signature. This will lead to a compilation error. The correct way to create a new String object is by using the new keyword, as shown in the second line.

In Java, a variable’s scope is defined by the surrounding curly braces {}. If you define a variable inside a set of curly braces, it is only accessible within those braces. Consider the following code:

if (somethingIsTrue()) {
    String message = "Everything is fine";
} else {
    String message = "We have an error";
}
System.out.println(message);

In this code, both message variables are out of scope when System.out.println(message); is executed. This will result in a compilation error because message is not visible in the outer scope.

It’s important to note that Java does not have free() or delete operators like some other languages do. Instead, it relies on variable scope to determine when variables are no longer needed.

To fix this issue, you can declare the message variable outside the if statement and then assign a value to it inside the if block:

String message = "We have an error";
if (somethingIsTrue()) {
    message = "Everything is fine";
}
System.out.println(message);

This way, message is defined in the outer scope and can be accessed and modified inside the if statement without any scope issues.