Why do these classes only compile when in the same file?

I have this directory c:\abcd it has no files in it, but has a subdirectory efgh.

C:\abcd>dir
 Volume in drive C has no label.
 Volume Serial Number is B411-D580

 Directory of C:\abcd

02/08/2014  06:57 PM    <DIR>          .
02/08/2014  06:57 PM    <DIR>          ..
02/08/2014  07:01 PM    <DIR>          efgh
               0 File(s)              0 bytes
               3 Dir(s)   1,990,201,344 bytes free

The efgh subdirectory, as you can see, has 3 files. Blah.java, Greeter.java and comb.java

Blah.java has a class, Greeter.java has a class.

comb.java happens to contain the code of Blah.java and Greeter.java, so it has two classes. It's superfluous but it's to demonstrate something. Which is a case of comb compiling while one of the individual files (Blah.java) does not.

C:\abcd>cd efgh

C:\abcd\efgh>dir
 Volume in drive C has no label.
 Volume Serial Number is B411-D580

 Directory of C:\abcd\efgh

02/08/2014  07:01 PM    <DIR>          .
02/08/2014  07:01 PM    <DIR>          ..
02/08/2014  07:00 PM               130 Blah.java
02/08/2014  07:00 PM               216 comb.java
02/08/2014  07:00 PM                99 Greeter.java
               3 File(s)            445 bytes
               2 Dir(s)   1,990,201,344 bytes free

These are the contents of the three files

C:\abcd\efgh>type Blah.java

package efgh;

class Blah {

    Greeter greeter = new Greeter();

    public static void main(String[] args) {}

}

C:\abcd\efgh>type Greeter.java

package efgh;

class Greeter {

    public static void main(String[] args) {}

}    

C:\abcd\efgh>type comb.java

package efgh;

class Blah {

    Greeter greeter = new Greeter();

    public static void main(String[] args) {}

}

class Greeter {

    public static void main(String[] args) {}

}

C:\abcd\efgh>javac comb.java

So, comb.java compiles.

I see it produced Blah.class and Greeter.class which is further proof that it compiled, but I'll now delete those .class files. As that makes my point, that javac comb.java works.

C:\abcd\efgh>dir
 Volume in drive C has no label.
 Volume Serial Number is B411-D580

 Directory of C:\abcd\efgh

02/08/2014  07:02 PM    <DIR>          .
02/08/2014  07:02 PM    <DIR>          ..
02/08/2014  07:02 PM               341 Blah.class
02/08/2014  07:00 PM               130 Blah.java
02/08/2014  07:00 PM               216 comb.java
02/08/2014  07:02 PM               261 Greeter.class
02/08/2014  07:00 PM                99 Greeter.java
               5 File(s)          1,047 bytes
               2 Dir(s)   1,990,201,344 bytes free

C:\abcd\efgh>del *.class

Now I'll compile them individually. And it doesn't work so well.. compiling Blah.java gives an error.

C:\abcd\efgh>dir
 Volume in drive C has no label.
 Volume Serial Number is B411-D580

 Directory of C:\abcd\efgh

02/08/2014  07:03 PM    <DIR>          .
02/08/2014  07:03 PM    <DIR>          ..
02/08/2014  07:00 PM               130 Blah.java
02/08/2014  07:00 PM               216 comb.java
02/08/2014  07:00 PM                99 Greeter.java
               3 File(s)            445 bytes
               2 Dir(s)   1,990,201,344 bytes free

C:\abcd\efgh>javac Greeter.java

C:\abcd\efgh>javac Blah.java
Blah.java:5: error: cannot find symbol
   Greeter greeter = new Greeter();
   ^
  symbol:   class Greeter
  location: class Blah
Blah.java:5: error: cannot find symbol
   Greeter greeter = new Greeter();
                         ^
  symbol:   class Greeter
  location: class Blah
2 errors

I know that technically, my classpath isn't set right. Because, given that I have no classpath variable set, it is defaulting to a classpath of current directory. And given the package names, the classpath should be c:\abcd. And if I set it to that then it compiles blah.java individually.

C:\abcd\efgh>javac -cp c:\abcd Blah.java

But that still doesn't explain why comb.java compiles fine with no switches:

C:\abcd\efgh>javac comb.java

C:\abcd\efgh>

I understand the package statement in comb.java applies to the Blah class and the Greeter class. I don't see why when compiling comb.java I don't need to set the classpath specially, whereas I do when compiling javac Blah.java.

Jon Skeet
people
quotationmark

You're compiling without specifying a output "root" for the compiler to build up a package hierarchy from - so it just puts the class files in the same directory as the source files. Then it tries to find the classes later on, but it can't find classes in the efgh package, because you're already in the efgh directory and you haven't set your classpath correctly.

Options:

  • Compile from the source root:

    c:\abcd> javac efgh\Greeter.java
    c:\abcd> javac efgh\Blah.java
    
  • Compile specifying an output root, and putting that on the classpath the second time:

    c:\abcd> javac -d ..\bin Greeter.java
    c:\abcd> javac -d ..\bin -cp ..\bin Blah.java
    
  • Both!

    c:\abcd> javac -d bin efhj\Greeter.java
    c:\abcd> javac -d bin -cp bin efgh\Blah.java
    

As for which comb.java compiles - it doesn't have any dependencies on other classes, so the compiler doesn't need to try to find efgh.Greeter or efgh.Blah. It's only failing for Blah because it's failing to find efgh.Greeter.

people

See more on this question at Stackoverflow